Introduzione
Ricordo che un Web server quando riceve una richiesta CGI, deve eseguire un programma completamente diverso e restituire il risultato del processo al Web browser.
Bisogna considerare che i CGI sono stati concepiti poco dopo la nascita del Web, in un periodo in cui i Web server ricevevano un numero daccessi limitato e quindi la velocita' di risposta ad ogni richiesta dei client, non era una condizione cosi' rilevante.
Con l'aumento della "popolazione" Internet, i Web server devono soddisfare un numero sempre crescente di richieste e spesso devono servire svariati accessi contemporaneamente, facendo si', che la velocita' di risposta, diventa una condizione non trascurabile.
I Servlet, ci permettono di ottenere delle performance di esecuzione notevolmente migliori rispetto ai CGI, poiche', come ci ha detto Fabrizio Giudici l'attivazione di un Servlet non richiede la creazione di un nuovo processo per ogni richiesta, ma e eseguito in parallelo all'interno dello stesso processo del web-server.
Approfondiamo questa caratteristica importante dei Servlet, che li contraddistinguono dai CGI e cioe' quella di non richiedere l'attivazione di un nuovo processo.
Un Servlet, facendo parte integrante dello stesso processo del Web server, si puo' immaginare che sia una procedura dello stesso server.
Questo perche il Web server, effettua un caricamento dinamico della classe Servlet, durante lo start-up, oppure come conseguenza della prima richiesta al Servlet da parte di un client, dopodiche questi rimane residente in memoria ed a disposizione delle successive richieste.
Poiche il tempo di attivazione di un Servlet, in pratica e quello del caricamento dinamico di una qualunque classe e poiche una volta effettuato, non deve essere piu ripetuto, si puo capire perche, il tempo di chiamata di un Servlet, e' assimilabile a quello di una chiamata ad una procedura all'interno di un processo.
Per comprendere meglio la differenza con i CGI, si consideri che il tempo di attivazione ed inizializzazione di un CGI deve essere calcolato per ogni richiesta del client, mentre il tempo necessario al caricamento ed all'inizializzazione di un Servlet, di per se' minimo, essendo effettuato una sola volta, e ripartito per tutte le successive richieste dei client. Lascio immaginare a voi quali possono essere i guadagni, in termini di prestazioni che si ottengono.
Non e' tutto, perche, essendo il metodo init() del Servlet chiamato solamente in fase di inizializzazione, tutte quelle operazioni che non necessitano di esecuzione ad ogni richiesta dei client, si puo inserirle proprio in questo metodo.
Quindi, lincremento di prestazioni, confrontato con i CGI, non si limita ai soli tempi di attivazione.
La persistenza del Servlet dopo la sua inizializzazione ci permette, infatti, di gestire molte problematiche in modo piu efficiente.
Se si pensa, infatti, alla necessita' da parte dei clients di fare accesso ai Database, si puo' subito intuire che l'operazione di connessione al Database, che comporta un rilevante dispendio di tempo e che con i CGI e effettuata ad ogni richiesta dei clients, con i Servlet e' sufficiente inserirla nel metodo init(), facendo si' che non venga piu' ripetuta per tutte le successive richieste.
E evidente, che questa gestione, e piu' 'pulita' e ci permette di ottenere risposte alle richieste dei clients motlo veloci.
La deallocazione di un Servlet avviene in genere allo shut-down del Web server, in conseguenza del quale e chiamato il metodo destroy().
Inoltre la deallocazione del Servlet, avviene anche quando si elimina il suo Alias dalla tabella di configurazione dei "Servlet Alias" ed infine, quando si preme esplicitamente il button "Unload" dal menu' di gestione Servlet del Web server, funzionalita' che ci permette di modificare un Servlet e attivarlo senza fare lo shut-down del Web server.
Il Web server che ho utilizzato e' il Java Web Server 1.01 (SUN) e quindi la tabella di Servlet Alias ed il buttono "Unload" sono caratteristiche di questo Web Server.
Per quanto riguarda altri Web server che implementano i Servlet, bisogna fare riferimento alle rispettive documentazioni.
Un'ultima considerazione, ci si chiedera' cosa avviene quando diverse richieste accedono allo stesso Servlet ?
Ogni richiesta ad un Servlet e gestita da un "handler Threads", quindi ci si puo' trovare nella situazione in cui piu' "handler Threads" fanno uso concorrente dello stesso Servlet.
In pratica, ogni richiesta del client attiva un Servlet Threads e molti metodi service() possono essere contemporaneamente attivi.
L'interfaccia Servlet, non assicura una protezione dagli accessi concorrenti e quindi e' opportuno, implementare il metodo service(), in modo da proteggere i dati da accessi concorrenti, utilizzando la parola chiave synchronized.
La soluzione piu' semplice e' definire il metodo service come synchronized, ma nel nostro caso, non ne abbiamo la necessita'.
Infatti, dovendo effettuare una connessione al Database, potremmo trovarci in una situazione di concorrenza se dovessimo definire l'oggetto Connection, come variabile di instanza della classe ed effettuare la connessione al Database, all'interno del metodo service().
Poiche' la connessione al Database e effettuata nel metodo init() ed essendo quest'ultimo chiamato una sola volta, non abbiamo problemi di concorrenza
Esempio.
Vediamo quindi un esempio di Servlet, che gestisce la connessione ad un Database tramite il driver JDBC e che genera dinamicamente una pagina HTML, utilizzando il risultato di una query al Database.
Questo esempio e fornito con il Java Web Server 1.01 (SUN).
Commentero' le parti piu' importanti ed indichero le modifiche da apportare per raggiungere gli obiettivi proposti in questo articolo.
/*
* @(#)DBServlet.java 1.6 97/06/13
*
* Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
* CopyrightVersion 1.0
*/
import
javax.servlet.*;
import
javax.servlet.http.*;
import
java.io.*;
import
java.net.*;
import
java.sql.*;
/*
* DBServlet
*
*
This
servlet demonstrates how to use JDBC stack
* and access database using Jeeves servlet. In order to run this
* servlet, CLASSPATH, LD_LIBRARY_PATH, ODBC_INI, and other related
* environment variables must be set prior to running the Jeeves
* server since the name resolution
for
JDBC stack library happens
* during the run time. Part of the code was contributed by
* Donald E. McCollam
*
* @author Jongyoon Lee
* @version 1.6, 06/13/97
*/
// Evidenziero' le modifiche da effettuare con il carattere '$' e le
// le parti da sostituire con il carattere '#'.
// Bisogna considerare che nella versione originale del servlet la connessione viene
// effettuata nel metodo service(), poiche' si ha la necessita' di ottenere i dati
// necessari alla connessione dal client e quindi
// inevitabilmente la connessione deve essere effettuata dopo aver acquisito
// i valori dei parametri.
// Volutamente non cancello la parte di codice originale, poiche'
// ci si puo' trovare nella situazione di dover acquisire i parametri per
// la connessione dal client.
// Quindi si potra' scegliere una soluzione o l'altra semplicemente eliminando
// le parti di codice contrassegnate dal carattere '#', oppure '$'.
public
class
DBServlet
extends
HttpServlet {
// Ecco il metodo init() dove, come ho spiegato nell'articolo,
// e' possibile effettuare l' operazione di connessione al Database.
// Per fare cio' e' necessario dichiarare l' oggetto Connection come
// variabile di istanza della classe.
// L' oggetto Connection, viene dichiarato qui', anziche' nel blocco del
// metodo service()
$ Connection con;
// Questo e' il metodo init() originale.
# /*
# * init method
# */
#
public void
init(ServletConfig servletConfig)
throws
ServletException {
#
super
.init(servletConfig);
# }
// Questo e' il metodo init() con la connessione al DB.
// Si noti che l'output degli errori viene inviato allo
// standard output, anziche' al client, poiche', a questo
// punto non si e' in possesso dei canali di I/O per inviare
// informazioni al client.
// I canalidi I/O vengono acquisiti nel metodo service().
$ /*
$ * init method
$ */
$ public void init() throws ServletException {
$ try
$ {
$ Class.forName(
"sun.jdbc.odbc.JdbcOdbcDriver"
);
$ con = DriverManager.getConnection(
"jdbc:odbc:MIODB"
,
"MIAUSER"
,
"MIAPASSWORD"
);
$ }
$ catch (SQLException ex) {
$ System.out.println (
"\n*** SQLException caught ***\n"
);
$ while (ex != null) {
$ System.out.println (
"SQLState: "
+ ex.getSQLState ());
$ System.out.println (
"Message: "
+ ex.getMessage ());
$ System.out.println (
"Vendor: "
+ ex.getErrorCode ());
$ ex = ex.getNextException ();
$ System.out.println ("");
$ }
$ }
$ catch (java.lang.Exception jle) {
$ jle.printStackTrace();
$ }
$ }
// E' necessario, implementare il metodo destroy(), per poter
// effettuare la chiusura della connessione col Database.
// Bisogna considerare che anche quando viene chiamato il metodo destroy(),
// non siamo in possesso dei canali di I/O con il client, poiche'
// il metodo, non viene chiamato in conseguenza di una richiesta del client,
// ma nei casi che ho evidenziato nell' articolo.
// Quindi le eventuali informazioni di errore non devono essere inviate a clients.
$
public
void
destroy()
$ {
$ try
$ {
$ con.close();
$ }
$
catch
(SQLException ex) {
$ System.out.println (
"\n*** SQLException caught ***\n"
);
$
while
(ex != null) {
$ System.out.println (
"SQLState: "
+ ex.getSQLState ());
$ System.out.println (
"Message: "
+ ex.getMessage ());
$ System.out.println (
"Vendor: "
+ ex.getErrorCode ());
$ ex = ex.getNextException ();
$ System.out.println (
""
);
$ }
$ }
$ }
/*
* service method
*/
public
void
service(HttpServletRequest req, HttpServletResponse res)
throws
ServletException, IOException {
String stack, username, password, query;
// L' oggetto Connection, e' stato spostato, ed e' stato dichiarato come
// variabile di istanza della classe DBServlet.
# Connection con;
PrintStream out;
// fetch the parameters
stack = req.getParameter(
"stack"
);
username = req.getParameter(
"username"
);
password = req.getParameter(
"password"
);
query = req.getParameter(
"query"
);
res.setContentType(
"text/html"
);
out =
new
PrintStream(res.getOutputStream());
printPageHeader(out);
// if no parameters, just print the form
if
(stack == null || username == null || password == null ||
query == null) {
printPageFooter(out);
return
;
}
String url =
"jdbc:odbc:"
+stack;
out.println(
"<hr><h3>Previous Query</h3>""
);
out.println(
"<pre>"
);
out.println(
"Database Stack : "
+stack);
out.println(
" User ID : "
+username);
out.println(
" Password : "
+password);
out.println(
" Query : "
+query);
out.println(
"</pre>"
);
try
{
// find the jdbc stack
// Il caricamento del bridge JDBC-ODBC e la connessione
// sono stati spostati nel metodo init()
#
Class
.forName(
"sun.jdbc.odbc.JdbcOdbcDriver"
);
#
// Class.forName("jdbc.odbc.JdbcOdbcDriver")
;
#
#
// get the connection to the database
# con = DriverManager.getConnection(url, username, password);
out.println(
"<hr>"
);
out.println(
"<h3>ODBC Driver and Database Messages</h3>"
);
checkForWarning(con.getWarnings(), out);
DatabaseMetaData dma = con.getMetaData();
out.println(
"Connected to "
+ dma.getURL() +
"<br>"
);
out.println(
"Driver "
+ dma.getDriverName() +
"<br>"
);
out.println(
"Version "
+ dma.getDriverVersion() +
"<br>"
);
// create and execute the query
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
// print out the result
dispResultSet(rs, out);
rs.close(); stmt.close();
// La chiusura della connessione viene effettuata nel metodo destroy()
# con.close();
out.println(
"<hr>"
);
}
catch
(SQLException ex) { out.println(
"<hr>*** SQLException caught ***<p>"
);
while
(ex != null) { out.println(
"SQLState: "
+ ex.getSQLState() +
"<br>"
);
out.println(
"Message: "
+ ex.getMessage() +
"<br>"
);
out.println(
"Vendor: "
+ ex.getErrorCode() +
"<br>"
);
ex = ex.getNextException();
}
}
catch
(java.lang.Exception ex) { ex.printStackTrace(); }
printPageFooter(out); }
/*
* returns the servlet info
*/
public
String getServletInfo() {
return
"This is a simple servlet that shows how to use JDBC"
;
}
/*
* check if the database server has anything to say
*/
private
void
checkForWarning(SQLWarning warn, PrintStream out)
throws
SQLException {
boolean
rc = false;
if
(warn != null) {
out.println(
"<hr>*** Warning ***<p>"
);
rc = true;
while
(warn != null) {
out.println(
"SQLState: "
+ warn.getSQLState() +
"<br>"
);
out.println(
"Message: "
+ warn.getMessage() +
"<br>"
);
out.println(
"Vendor: "
+ warn.getErrorCode() +
"<br>"
);
warn = warn.getNextWarning();
}
}
}
/*
* display the result in html table format
*/
private
void
dispResultSet(ResultSet rs, PrintStream out)
throws
SQLException
{
int
i;
// metadata can supply information about the schema
ResultSetMetaData rsmd = rs.getMetaData();
int
numCols = rsmd.getColumnCount();
out.println(
"<hr>"
);
out.println(
"<h3>Database Columns and Data</h3>"
);
out.println(
"<table border=3>"
);
out.println(
"<tr>"
);
for
(i=1; i<=numCols; i++) {
out.println(
"<th>"
+ rsmd.getColumnLabel(i) +
"</th>"
);
}
out.println(
"</tr>"
);
// for entire data
while
(rs.next()) {
out.println(
"<tr>"
);
// for one row
for
(i=1; i<=numCols; i++) {
dispElement(rs, rsmd.getColumnType(i), out, i);
}
out.println(
"<tr>"
);
}
out.println(
"</table>"
);
}
// print one element
private
void
dispElement(ResultSet rs,
int
dataType, PrintStream out,
int
col)
throws
SQLException {
// ask for data depending on the datatype
switch
(dataType) {
case
Types.DATE:
java.sql.Date date = rs.getDate(col);
out.println(
"<th>"
+ date.toString() +
"</th>"
);
break
;
case
Types.TIME:
java.sql.Time time = rs.getTime(col);
out.println(
"<th>"
+ time.toString() +
"</th>"
);
break
;
case
Types.TIMESTAMP:
java.sql.Timestamp timestamp = rs.getTimestamp(col);
out.println(
"<th>"
+ timestamp.toString() +
"</th>"
);
break
;
case
Types.CHAR:
case
Types.VARCHAR:
case
Types.LONGVARCHAR:
String str = rs.getString(col);
out.println(
"<th>"
+ str +
"</th>"
);
break
;
case
Types.NUMERIC:
case
Types.DECIMAL:
java.math.BigDecimal numeric = rs.getBigDecimal(col, 10);
out.println(
"<th>"
+ numeric.toString() +
"</th>"
);
break
;
case
Types.BIT:
boolean
bit = rs.getBoolean(col);
out.println(
"<th>"
+
new
Boolean
(bit) +
"</th>"
);
break
;
case
Types.TINYINT:
byte
tinyint = rs.getByte(col);
out.println(
"<th>"
+
new
Integer(tinyint) +
"</th>"
);
break
;
case
Types.SMALLINT:
short
smallint = rs.getShort(col);
out.println(
"<th>"
+
new
Integer(smallint) +
"</th>"
);
break
;
case
Types.INTEGER:
int
integer = rs.getInt(col);
out.println(
"<th>"
+
new
Integer(integer) +
"</th>"
);
break
;
case
Types.BIGINT:
long
bigint = rs.getLong(col);
out.println(
"<th>"
+
new
Long
(bigint) +
"</th>"
);
break
;
case
Types.REAL:
float
real = rs.getFloat(col);
out.println(
"<th>"
+
new
Float
(real) +
"</th>"
);
break
;
case
Types.FLOAT:
case
Types.DOUBLE:
double
longreal = rs.getDouble(col);
out.println(
"<th>"
+
new
Double
(longreal) +
"</th>"
);
break
;
case
Types.BINARY:
case
Types.VARBINARY:
case
Types.LONGVARBINARY:
byte
[] binary = rs.getBytes(col);
out.println(
"<th>"
+
new
String(binary, 0) +
"</th>"
);
break
;
}
}
private
void
printPageHeader(PrintStream out) {
out.println(
"<html>"
);
out.println(
"<head>"
);
out.println(
"<tltle>Jeeves Servlet Test</title>"
);
out.println(
"</head>"
);
out.println(
"<body>"
);
out.println(
"<center><font size=5>" + "<b>Jeeves Database Servlet</b>" + "</font></center>"
);
out.println(
"<hr>"
);
out.println(
"<form action=\"/servlet/dbServlet\" method=\"get\">"
);
out.println(
"<pre>"
);
out.println(
"Database Stack : <INPUT type=textarea name=stack>"
);
out.println(
" User ID : <INPUT type=textarea name=username>"
);
out.println(
" Password : <INPUT type=textarea name=password>"
);
out.println(
" Query : <INPUT type=textarea name=query>"
);
out.println(
"/PRE"
);
out.println(
"<INPUT type=submit>"
);
out.println(
"</FORM>"
);
}
private
void
printPageFooter(PrintStream out) {
out.println(
"</body>"
);
out.println(
"</html>"
);
out.flush();
}
}
Bibliografia.
Fonti : "Developing Java Servlets" (William Crawford)
Java Web Server 1.01 Documentation
Software: Java Web Server 1.x/2.x (SUN) http://java.sun.com/products/java-server/webserver/index.html
WebSite 2.0 (O'Reilly & Associates, Inc.) http://software.ora.com/
Java Servlet Development Kit (SUN) http://java.sun.com/products/java-server/webserver/index.html
ServletExpress(IBM) http://www.ibm.com/java/servexp/
JRUN (LiveSoftware) http://www.livesoftware.com
Domenico di Girolamo - Freelance Software Developer - mdg@iol.it
Il mio interesse per i computers e inizato, quando, ancora fanciullo, dopo aver giocato una partita a frogger nella sala giochi di turno incappai nella vetrina di un negozio di elettronica, dove rimasi affascinato e incredulo nel vedere che lo stesso gioco che mi aveva costretto poco tempo prima ad inserire preziose monetine in un buco senza ritorno, era proiettato sullo schermo di un normale televisore, da uno scatolotto assomigliante ad una macchina da scrivere. Il mio stupore aumento quando entrando nel negozio mi accorsi che il ranocchio non solo era proiettato sul televisore, ma poteva anche essere manovrato esattamente come facevo poco tempo prima con larmadio della sala giochi. Lo stupore divento euforia quando dopo aver giocato una partita con lo scatolotto, mi accorsi che la temuta scritta GAME OVER era diventata del tutto innocua, poiche mi basto premere un tasto e la partita ricomincio senza dover inserire una nuova preziosa monetina. Lo scatolotto aveva una scritta allora a me sconosciuta ed ora probabilmente sconosciuta a qualcuno , COMMODORE 64. Nel giro di qualche giorno, lo scatolotto si trasferi dal negozio alla mia scrivania. Ho maturato diversi anni di attivita in vari settori, principalmente in quello dellautomazione industriale ed ho utilizzato come linguaggio di sviluppo soprattutto il C. Attualmente il mio interesse si e diretto verso la tecnologia JAVA e sono socio JIA (Java Italian Association) nonche moderatore della Mailing List jia@infomedia.it.
[](index.htm)
