MokaByte
Numero 16
- Febbraio 1998
|
|||
|
|
||
Domenico Di Girolamo |
|
||
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 d’accessi 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, l’incremento
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
Bibliografia.
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(ServletConfig servletConfig) throws ServletException {
$ super.init(servletConfig);
$ 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();
}
}
"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.
|
|
||
MokaByte ricerca
nuovi collaboratori.
|
||
|