 |
 |
 |
15) Informatique distribuée |
|
 |
|
Texte original |
 |
Traducteur :
Jean-Pierre Vidal, Alban Peignier |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
The following program uses the
CIDConnect and CIDSQL information to load the JDBC driver, make a
connection to the database, and then create the table structure diagrammed
above. To connect with the database, you call the static method
DriverManager.getConnection( ), passing it the database URL, the
user name, and a password to get into the database. You get back a
Connection object that you can use to query and manipulate the database.
Once the connection is made you can simply push the SQL to the database, in this
case by marching through the CIDSQL array. However, the first time this
program is run, the “drop table” command will fail, causing an
exception, which is caught, reported, and then ignored. The reason for the
“drop table” command is to allow easy experimentation: you can
modify the SQL that defines the tables and then rerun the program, causing the
old tables to be replaced by the new.
|
 |
Le programme qui suit utilise l'information de CIDConnect et
CIDSQL pour charger le driver JDBC, se connecter à la base de données, puis créer la
structure de la table conforme au diagramme ci-dessus. Pour se connecter à la base, on appelle la
méthode static DriverManager.getConnection( ), en lui
passant l'URL de base de données, le nom d'utilisateur et un mot de passe. On obtient en retour un
objet Connection que l'on peut utiliser pour interroger et manipuler la base. La
connexion établie, il suffit d'envoyer le SQL à la base, dans ce cas en balayant le tableau
CIDSQL. Toutefois, au premier lancement du programme, la commande « drop
table » échouera, lançant une exception, qui sera capturée, rapportée, puis ignorée. La
commande « drop table » n'a d'autre but que de permettre une expérimentation plus
facile : on peut modifier le code SQL définissant les tables puis exécuter à nouveau le
programme, les anciennes tables étant remplacées par les nouvelles.
|
 |
 |
 |
In this example, it makes sense to let
the exceptions be thrown out to the console:
|
 |
Dans cet exemple, il est intéressant de laisser les exceptions s'afficher
sur la console :
|
 |
 |
 |
//: c15:jdbc:CIDCreateTables.java
// Creates database tables for the
// community interests database.
import java.sql.*;
public class CIDCreateTables {
public static void main(String[] args)
throws SQLException, ClassNotFoundException,
IllegalAccessException {
// Load the driver (registers itself)
Class.forName(CIDConnect.dbDriver);
Connection c = DriverManager.getConnection(
CIDConnect.dbURL, CIDConnect.user,
CIDConnect.password);
Statement s = c.createStatement();
for(int i = 0; i < CIDSQL.sql.length; i++) {
System.out.println(CIDSQL.sql[i]);
try {
s.executeUpdate(CIDSQL.sql[i]);
} catch(SQLException sqlEx) {
System.err.println(
"Probably a 'drop table' failed");
}
}
s.close();
c.close();
}
} ///:~
|
 |
//: c15:jdbc:CIDCreateTables.java // Crée les tables d'une base de données pour la // «community interests database». import java.sql.*;
public class CIDCreateTables { public static void main(String[ « args) throws SQLException, ClassNotFoundException, IllegalAccessException { // Charger le driver (qui s'enregistrera lui-même) Class.forName(CIDConnect.dbDriver); Connection c = DriverManager.getConnection( CIDConnect.dbURL, CIDConnect.user, CIDConnect.password); Statement s = c.createStatement(); for(int i = 0; i < CIDSQL.sql.length; i++) { System.out.println(CIDSQL.sql[i]); try { s.executeUpdate(CIDSQL.sql[i]); } catch(SQLException sqlEx) { System.err.println( "Probably a 'drop table' failed"); } } s.close(); c.close(); } } ///:~
|
 |
 |
 |
Note that all changes in the database can
be controlled by changing Strings in the CIDSQL table, without
modifying CIDCreateTables.
|
 |
Remarquons que les modifications de la base peuvent être contrôlées en
changeant Strings dans la table CIDSQL, sans modifier
CIDCreateTables.
|
 |
 |
 |
executeUpdate( ) will usually
return the number of rows that were affected by the SQL statement.
executeUpdate( ) is more commonly used to execute
INSERT,
UPDATE, or
DELETE
statements that modify one or more rows. For statements such as
CREATE
TABLE, DROP
TABLE, and
CREATE
INDEX, executeUpdate( ) always
returns zero.
|
 |
La méthode executeUpdate( ) renvoie généralement le
nombre d'enregistrements affectés par l'instruction SQL. Elle est très souvent utilisée pour
exécuter des instructions INSERT, UPDATE, ou
DELETEmodifiant une ou plusieurs
lignes. Pour les instructions telles que CREATE
TABLE, DROP
TABLE, et CREATE
INDEX, executeUpdate( )renvoie toujours
zéro.
|
 |
 |
 |
To test the database, it is loaded with
some sample data. This requires a series of
INSERTs
followed by a
SELECT to
produce result set. To make additions and changes to the test data easy, the
test data is set up as a two-dimensional array of Objects, and the
executeInsert( ) method can then use the information in one row of
the table to create the appropriate SQL command.
|
 |
Pour tester la base, celle-ci est chargée avec quelques données exemples.
Ceci est réalisé au moyen d'une série d'INSERTface="Georgia">suivie d'un SELECTafin de produire le jeu de données. Pour effectuer facilement des additions et des
modifications aux données de test, ce dernier est construit comme un tableau
d'Objectà deux dimensions, et la méthode executeInsert( )
peut alors utiliser l'information d'une ligne de la table pour construire la commande SQL
appropriée.
|
 |
 |
 |
//: c15:jdbc:LoadDB.java
// Loads and tests the database.
import java.sql.*;
class TestSet {
Object[][] data = {
{ "MEMBERS", new Integer(1),
"dbartlett", "Bartlett", "David",
"123 Mockingbird Lane",
"Gettysburg", "PA", "19312",
"123.456.7890", "bart@you.net" },
{ "MEMBERS", new Integer(2),
"beckel", "Eckel", "Bruce",
"123 Over Rainbow Lane",
"Crested Butte", "CO", "81224",
"123.456.7890", "beckel@you.net" },
{ "MEMBERS", new Integer(3),
"rcastaneda", "Castaneda", "Robert",
"123 Downunder Lane",
"Sydney", "NSW", "12345",
"123.456.7890", "rcastaneda@you.net" },
{ "LOCATIONS", new Integer(1),
"Center for Arts",
"Betty Wright", "123 Elk Ave.",
"Crested Butte", "CO", "81224",
"123.456.7890",
"Go this way then that." },
{ "LOCATIONS", new Integer(2),
"Witts End Conference Center",
"John Wittig", "123 Music Drive",
"Zoneville", "PA", "19123",
"123.456.7890",
"Go that way then this." },
{ "EVENTS", new Integer(1),
"Project Management Myths",
"Software Development",
new Integer(1), new Float(2.50),
"2000-07-17 19:30:00" },
{ "EVENTS", new Integer(2),
"Life of the Crested Dog",
"Archeology",
new Integer(2), new Float(0.00),
"2000-07-19 19:00:00" },
// Match some people with events
{ "EVTMEMS",
new Integer(1), // Dave is going to
new Integer(1), // the Software event.
new Integer(0) },
{ "EVTMEMS",
new Integer(2), // Bruce is going to
new Integer(2), // the Archeology event.
new Integer(0) },
{ "EVTMEMS",
new Integer(3), // Robert is going to
new Integer(1), // the Software event.
new Integer(1) },
{ "EVTMEMS",
new Integer(3), // ... and
new Integer(2), // the Archeology event.
new Integer(1) },
};
// Use the default data set:
public TestSet() {}
// Use a different data set:
public TestSet(Object[][] dat) { data = dat; }
}
public class LoadDB {
Statement statement;
Connection connection;
TestSet tset;
public LoadDB(TestSet t) throws SQLException {
tset = t;
try {
// Load the driver (registers itself)
Class.forName(CIDConnect.dbDriver);
} catch(java.lang.ClassNotFoundException e) {
e.printStackTrace(System.err);
}
connection = DriverManager.getConnection(
CIDConnect.dbURL, CIDConnect.user,
CIDConnect.password);
statement = connection.createStatement();
}
public void cleanup() throws SQLException {
statement.close();
connection.close();
}
public void executeInsert(Object[] data) {
String sql = "insert into "
+ data[0] + " values(";
for(int i = 1; i < data.length; i++) {
if(data[i] instanceof String)
sql += "'" + data[i] + "'";
else
sql += data[i];
if(i < data.length - 1)
sql += ", ";
}
sql += ')';
System.out.println(sql);
try {
statement.executeUpdate(sql);
} catch(SQLException sqlEx) {
System.err.println("Insert failed.");
while (sqlEx != null) {
System.err.println(sqlEx.toString());
sqlEx = sqlEx.getNextException();
}
}
}
public void load() {
for(int i = 0; i< tset.data.length; i++)
executeInsert(tset.data[i]);
}
// Throw exceptions out to console:
public static void main(String[] args)
throws SQLException {
LoadDB db = new LoadDB(new TestSet());
db.load();
try {
// Get a ResultSet from the loaded database:
ResultSet rs = db.statement.executeQuery(
"select " +
"e.EVT_TITLE, m.MEM_LNAME, m.MEM_FNAME "+
"from EVENTS e, MEMBERS m, EVTMEMS em " +
"where em.EVT_ID = 2 " +
"and e.EVT_ID = em.EVT_ID " +
"and m.MEM_ID = em.MEM_ID");
while (rs.next())
System.out.println(
rs.getString(1) + " " +
rs.getString(2) + ", " +
rs.getString(3));
} finally {
db.cleanup();
}
}
} ///:~
|
 |
//: c15:jdbc:LoadDB.java // Charge et teste la base de données. import java.sql.*;
class TestSet { Object[][ « data = { { "MEMBERS", new Integer(1), "dbartlett", "Bartlett", "David", "123 Mockingbird Lane", "Gettysburg", "PA", "19312", "123.456.7890", "bart@you.net" }, { "MEMBERS", new Integer(2), "beckel", "Eckel", "Bruce", "123 Over Rainbow Lane", "Crested Butte", "CO", "81224", "123.456.7890", "beckel@you.net" }, { "MEMBERS", new Integer(3), "rcastaneda", "Castaneda", "Robert", "123 Downunder Lane", "Sydney", "NSW", "12345", "123.456.7890", "rcastaneda@you.net" }, { "LOCATIONS", new Integer(1), "Center for Arts", "Betty Wright", "123 Elk Ave.", "Crested Butte", "CO", "81224", "123.456.7890", "Go this way then that." }, { "LOCATIONS", new Integer(2), "Witts End Conference Center", "John Wittig", "123 Music Drive", "Zoneville", "PA", "19123", "123.456.7890", "Go that way then this." }, { "EVENTS", new Integer(1), "Project Management Myths", "Software Development", new Integer(1), new Float(2.50), "2000-07-17 19:30:00" }, { "EVENTS", new Integer(2), "Life of the Crested Dog", "Archeology", new Integer(2), new Float(0.00), "2000-07-19 19:00:00" }, // Met en relation personnes et événements { "EVTMEMS", new Integer(1), // Dave est mis en relation avec new Integer(1), // l'événement Software. new Integer(0) }, { "EVTMEMS", new Integer(2), // Bruce est mis en relation avec new Integer(2), // l'événement Archeology. new Integer(0) }, { "EVTMEMS", new Integer(3), // Robert est mis en relation avec new Integer(1), // l'événement Software... new Integer(1) }, { "EVTMEMS", new Integer(3), // ... et new Integer(2), // l'événement Archeology. new Integer(1) }, }; // Utiliser les données par défaut: public TestSet() {} // Utiliser un autre ensemble de données: public TestSet(Object[][ « dat) { data = dat; } }
public class LoadDB { Statement statement; Connection connection; TestSet tset; public LoadDB(TestSet t) throws SQLException { tset = t; try { // Charger le driver (qui s'enregistrera lui-même) Class.forName(CIDConnect.dbDriver); } catch(java.lang.ClassNotFoundException e) { e.printStackTrace(System.err); } connection = DriverManager.getConnection( CIDConnect.dbURL, CIDConnect.user, CIDConnect.password); statement = connection.createStatement(); } public void cleanup() throws SQLException { statement.close(); connection.close(); } public void executeInsert(Object[ « data) { String sql = "insert into " + data[0 « + " values("; for(int i = 1; i < data.length; i++) { if(data[i « instanceof String) sql += "'" + data[i « + "'"; else sql += data[i]; if(i < data.length - 1) sql += ", "; } sql += ')'; System.out.println(sql); try { statement.executeUpdate(sql); } catch(SQLException sqlEx) { System.err.println("Insert failed."); while (sqlEx != null) { System.err.println(sqlEx.toString()); sqlEx = sqlEx.getNextException(); } } } public void load() { for(int i = 0; i< tset.data.length; i++) executeInsert(tset.data[i]); } // Lever l'exception en l'envoyant vers la console: public static void main(String[ « args) throws SQLException { LoadDB db = new LoadDB(new TestSet()); db.load(); try { // Obtenir un ResultSet de la base chargée: ResultSet rs = db.statement.executeQuery( "select " + "e.EVT_TITLE, m.MEM_LNAME, m.MEM_FNAME "+ "from EVENTS e, MEMBERS m, EVTMEMS em " + "where em.EVT_ID = 2 " + "and e.EVT_ID = em.EVT_ID " + "and m.MEM_ID = em.MEM_ID"); while (rs.next()) System.out.println( rs.getString(1) + " " + rs.getString(2) + ", " + rs.getString(3)); } finally { db.cleanup(); } } } ///:~
|
 |
 |
 |
The TestSet class contains a
default set of data that is produced if you use the default constructor;
however, you can also create a TestSet object using an alternate data set
with the second constructor. The set of data is held in a two-dimensional array
of Object because it can be any type, including String or
numerical types. The executeInsert( ) method uses RTTI to
distinguish between String data (which must be quoted) and
non-String data as it builds the SQL command from the data. After
printing this command to the console, executeUpdate( ) is used to
send it to the database.
|
 |
La classe TestSet contient un ensemble de données par
défaut qui est mis en oeuvre lorsqu'on appelle le constructeur par défaut ; toutefois, il est
possible de créer au moyen du deuxième constructeur un objet TestSet utilisant un
deuxième ensemble de données. L'ensemble de données est contenu dans un tableau à deux dimensions
de type Object car il peut contenir n'importe quel type, y compris
String ou des types numériques. La méthode executeInsert( )
utilise RTTI pour différencier les données String (qui doivent être entre
guillemets) et les données non-String en construisant la commande SQL à partir des
données. Après avoir affiché cette commande sur la console, executeUpdate( )
l'envoie à la base de données.
|
 |
 |
 |
The constructor for LoadDB makes
the connection, and load( ) steps through the data and calls
executeInsert( ) for each record. cleanup( ) closes the
statement and the connection; to guarantee that this is called, it is placed
inside a finally clause.
|
 |
Le constructeur de LoadDB établit la connexion, et
load( )parcourt les données en appelant
executeInsert( )pour chaque enregistrement.
Cleanup( )termine l'instruction et la connexion ; tout ceci est placé
dans une clause finally afin d'en garantir l'appel.
|
 |
 |
 |
Once the database is loaded, an
executeQuery( ) statement produces a sample result set. Since the
query combines several tables, it is an example of a join.
|
 |
Une fois la base chargée, une instruction
executeQuery( )produit un ensemble résultat. La requête concernant plusieurs
tables, nous avons bien un exemple de base de données relationnelle.
|
 |
 |
 |
There is more JDBC information available
in the electronic documents that come as part of the Java distribution from Sun.
In addition, you can find more in the book JDBC Database Access with Java
(Hamilton, Cattel, and Fisher, Addison-Wesley, 1997). Other JDBC books
appear regularly.
|
 |
On trouvera d'autres informations sur JDBC dans les documents électroniques
livrés avec la distribution Java de Sun. Pour en savoir plus, consulter le livre JDBC Database
Access with Java (Hamilton, Cattel, and Fisher, Addison-Wesley, 1997). D'autres livres à
propos de JDBC sortent régulièrement.
|
 |
 |
 |
Servlets
|
 |
Les Servlets
|
 |
 |
 |
Client access from the Internet or
corporate intranets is a sure way to allow many users to access data and
resources
easily[74]. This
type of access is based on clients using the World Wide Web standards of
Hypertext Markup Language (HTML) and Hypertext Transfer Protocol (HTTP). The
Servlet API set abstracts a common solution framework for responding to HTTP
requests.
|
 |
Les accès clients sur l'Internet ou les intranets d'entreprise représentent
un moyen sûr de permettre à beaucoup d'utilisateurs d'accéder facilement aux données et
ressources [74]. Ce type d'accès est
basé sur des clients utilisant les standards du World Wide Web Hypertext Markup Language (HTML) et
Hypertext Transfer Protocol (HTTP). L'API Servlet fournit une abstraction pour un ensemble de
solutions communes en réponse aux requêtes HTTP.
|
 |
 |
 |
Traditionally, the way to handle a
problem such as allowing an Internet client to update a database is to create an
HTML page with text fields and a
“submit” button. The user types the appropriate information into the
text fields and presses the “submit” button. The data is submitted
along with a URL that tells the server what to do with the data by specifying
the location of a
Common
Gateway Interface (CGI) program that the server runs, providing the program with
the data as it is invoked. The CGI program is typically written in Perl, Python,
C, C++, or any language that can read from standard input and write to standard
output. That’s all that is provided by the Web server: the CGI program is
invoked, and standard streams (or, optionally for input, an environment
variable) are used for input and output. The CGI program is responsible for
everything else. First it looks at the data and decides whether the format is
correct. If not, the CGI program must produce HTML to describe the problem; this
page is handed to the Web server (via standard output from the CGI program),
which sends it back to the user. The user must usually back up a page and try
again. If the data is correct, the CGI program processes the data in an
appropriate way, perhaps adding it to a database. It must then produce an
appropriate HTML page for the Web server to return to the user.
|
 |
Traditionnellement, la solution permettant à un client Internet de mettre à
jour une base de données est de créer une page HTML contenant des champs texte et un bouton
« soumission ». L'utilisateur frappe l'information requise dans les champs texte puis
clique sur le bouton « soumission ». Les données sont alors soumises au moyen d'une URL
qui indique au serveur ce qu'il doit en faire en lui indiquant l'emplacement d'un programme Common
Gateway Interface (CGI) lancé par le serveur, qui prend ces données en argument. Le programme CGI
est généralement écrit en Perl, Python, C, C++, ou n'importe quel langage capable de lire sur
l'entrée standard et d'écrire sur la sortie standard. Le rôle du serveur Web s'arrête là : le
programme CGI est appelé, et des flux standard (ou, optionnellement pour l'entrée, une variable
d'environnement) sont utilisés pour l'entrée et la sortie. Le programme CGI est responsable de
toute la suite. Il commence par examiner les données et voir si leur format est correct. Si ce
n'est pas le cas, le programme CGI doit fournir une page HTML décrivant le problème ; cette
page est prise en compte par le serveur Web (via la sortie standard du programme CGI), qui la
renvoie à l'utilisateur. Habituellement, l'utilisateur revient à la page précédente et fait une
nouvelle tentative. Si les données sont correctes, le programme CGI traite les données de la
manière appropriée, par exemple en les ajoutant à une base de données. Il élabore ensuite une page
HTML appropriée que le serveur Web enverra à l'utilisateur.
|
 |
 |
 |
It would be ideal to go to a completely
Java-based solution to this problem—an applet on the client side to
validate and send the data, and a servlet on the server side to receive and
process the data. Unfortunately, although applets are a proven technology with
plenty of support, they have been problematic to use on the Web because you
cannot rely on a particular version of Java being available on a client’s
Web browser; in fact, you can’t rely on a Web browser supporting Java at
all! In an intranet, you can require that certain support be available, which
allows a lot more flexibility in what you can do, but on the Web the safest
approach is to handle all the processing on the server side and deliver plain
HTML to the client. That way, no client will be denied the use of your site
because they do not have the proper software installed.
|
 |
Afin d'avoir une solution basée entièrement sur Java, l'idéal serait
d'avoir côté client une applet qui validerait et enverrait les données, et côté serveur une servlet
qui les recevrait et les traiterait. Malheureusement, bien que les applets forment une technologie
éprouvée et bien supportée, leur utilisation sur le Web s'est révélée problématique car on ne peut
être certain de la disponibilité d'une version particulière de Java sur le navigateur Web du
client ; en fait, on ne peut même pas être certain que le navigateur Web supporte Java !
Dans un intranet, on peut exiger qu'un support donné soit disponible, ce qui apporte une certaine
flexibilité à ce qu'on peut faire, mais sur le Web l'approche la plus sûre est d'effectuer tout le
traitement du côté serveur puis de délivrer une page HTML au client. De cette manière, aucun client
ne se verra refuser l'utilisation de votre site simplement parce qu'il ne dispose pas dans sa
configuration du software approprié.
|
 |
 |
 |
Because servlets provide an excellent
solution for server-side programming support, they are one of the most popular
reasons for moving to Java. Not only do they provide a framework that replaces
CGI programming (and eliminates a number of thorny CGI problems), but all your
code has the platform portability gained from using Java, and you have access to
all the Java APIs (except, of course, the ones that produce GUIs, like
Swing).
|
 |
Parce que les servlets fournissent une excellente solution pour le support
de programmation côté serveur, ils représentent l'une des raisons les plus populaires pour passer à
Java. Non seulement ils fournissent un cadre pour remplacer la programmation CGI (et éliminer
nombre de problèmes CGI épineux), mais tout le code gagne en portabilité inter plate-forme en
utilisant Java, et l'on a accès à toutes les API Java (exceptées, bien entendu, celles qui
fournissent des GUI, comme Swing).
|
 |
 |
 |
The basic servlet
|
 |
Le servlet de base
|
 |
 |
 |
The architecture of the servlet API is
that of a classic service provider with a service( ) method through
which all client requests will be sent by the servlet container software, and
life cycle methods init( ) and destroy( ), which are
called only when the servlet is loaded and unloaded (this happens
rarely).
|
 |
L'architecture de l'API servlet est celle d'un fournisseur de services
classique comportant une méthode service( ) appartenant au software conteneur
de la servlet, chargée de recevoir toutes les requêtes client, et les méthodes liées au cycle de
vie, init( )et destroy( ), qui sont appelées seulement
lorsque la servlet est chargée ou déchargée (ce qui arrive rarement).
|
 |
 |
 |
public interface Servlet {
public void init(ServletConfig config)
throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req,
ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
|
 |
public interface Servlet { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; public String getServletInfo(); public void destroy(); }
|
 |
 |
 |
getServletConfig( )’s
sole purpose is to return a ServletConfig object that contains
initialization and startup parameters for this servlet.
getServletInfo( ) returns a string containing information about the
servlet, such as author, version, and copyright.
|
 |
La raison d'être de getServletConfig( )est de
renvoyer un objet ServletConfig contenant l'initialisation et les paramètres de
départ de cette servlet. La méthode getServletInfo( )renvoie une chaîne
contenant des informations à propos de la servlet, telles que le nom de l'auteur, la version, et le
copyright.
|
 |
 |
 |
The GenericServlet class is a
shell implementation of this interface and is typically not used. The
HttpServlet class is an extension of GenericServlet and is
designed specifically to handle the HTTP protocol— HttpServlet is
the one that you’ll use most of the time.
|
 |
La classe GenericServlet est une implémentation de cette
interface et n'est généralement pas utilisée. La classe HttpServlet est une
extension de GenericServlet, elle est explicitement conçue pour traiter le
protocole HTTP. C'est cette classe, HttpServlet, que vous utiliserez la plupart du
temps.
|
 |
 |
 |
The most convenient attribute of the
servlet API is the auxiliary objects that come along with the HttpServlet class
to support it. If you look at the service( ) method in the
Servlet interface, you’ll see it has two parameters:
ServletRequest and ServletResponse. With the HttpServlet
class these two object are extended for HTTP: HttpServletRequest and
HttpServletResponse. Here’s a simple example that shows the use of
HttpServletResponse:
|
 |
Les attributs les plus commodes de l'API servlet sont les objets
auxiliaires fournis par la classe HttpServlet. En regardant la méthode
service( )de l'interface Servlet, on constate qu'elle a deux
paramètres : ServletRequest et ServletResponse. Dans la
classe HttpServlet, deux objets sont développés pour HTTP :
HttpServletRequest and HttpServletResponse. Voici un exemple
simple montrant l'utilisation de HttpServletResponse :
|
 |
 |
 |
//: c15:servlets:ServletsRule.java
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class ServletsRule extends HttpServlet {
int i = 0; // Servlet "persistence"
public void service(HttpServletRequest req,
HttpServletResponse res) throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.print("<HEAD><TITLE>");
out.print("A server-side strategy");
out.print("</TITLE></HEAD><BODY>");
out.print("<h1>Servlets Rule! " + i++);
out.print("</h1></BODY>");
out.close();
}
} ///:~
|
 |
//: c15:servlets:ServletsRule.java import javax.servlet.*; import javax.servlet.http.*; import java.io.*;
public class ServletsRule extends HttpServlet { int i = 0; // «persistance» de Servlet public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); out.print("<HEAD><TITLE>"); out.print("A server-side strategy"); out.print("</TITLE></HEAD><BODY>"); out.print("<h1>Servlets Rule! " + i++); out.print("</h1></BODY>"); out.close(); } } ///:~
|
 |
 |
 |
ServletsRule is about as simple as
a servlet can get. The servlet is initialized only once by calling its
init( ) method, on loading the servlet after the servlet container
is first booted up. When a client makes a request to a URL that happens to
represent a servlet, the servlet container intercepts this request and makes a
call to the service( ) method, after setting up the
HttpServletRequest and HttpServletResponse objects.
|
 |
La classe ServletsRule est la chose la plus simple que
peut recevoir une servlet. La servlet est initialisée au démarrage en appelant sa méthode
init( ), en chargeant la servlet après que le conteneur de la servlet soit
chargé. Lorsqu'un client envoie une requête à une URL qui semble reliée à une servlet, le conteneur
de servlet intercepte cette demande et effectue un appel de la méthode
service( ), après avoir créé les objets HttpServletRequest
et HttpServletResponse.
|
 |
 |
 |
The main responsibility of the
service( ) method is to interact with the HTTP request that the
client has sent, and to build an HTTP response based on the attributes contained
within the request. ServletsRule only manipulates the response object
without looking at what the client may have sent.
|
 |
La principale responsabilité de la méthode
service( )est d'interagir avec la requête HTTP envoyée par le client, et de
construire une réponse HTTP basée sur les attributs contenus dans la demande. La méthode
ServletsRule se contente de manipuler l'objet réponse sans chercher à savoir ce
que voulait le client.
|
 |
 |
 |
After setting the content type of the
response (which must always be done before the Writer or OutputStream
is procured), the getWriter( ) method of the response object
produces a PrintWriter object, which is used for writing character-based
response data (alternatively, getOutputStream( ) produces an
OutputStream, used for binary response, which is only utilized in more
specialized solutions).
|
 |
Après avoir mis en place le type du contenu de la réponse (ce qui doit
toujours être fait avant d'initialiser Writer ou OutputStream),
la méthode getWriter( ) de l'objet réponse renvoie un objet
PrintWriter, utilisé pour écrire les données en retour sous forme de caractères
(de manière similaire, getOutputStream( )fournit un
OutputStream, utilisé pour les réponses binaires, uniquement dans des solutions
plus spécifiques).
|
 |
 |
 |
The rest of the program simply sends HTML
back to the client (it’s assumed you understand HTML, so that part is not
explained) as a sequence of Strings. However, notice the inclusion of the
“hit counter” represented by the variable i. This is
automatically converted to a String in the print( )
statement.
|
 |
Le reste du programme se contente d'envoyer une page HTML au client (on
suppose que le lecteur comprend le langage HTML, qui n'est pas décrit ici) sous la forme d'une
séquence de Strings. Toutefois, il faut remarquer l'inclusion du « compteur
de passages » représenté par la variable i. Il est automatiquement converti
en String dans l'instruction print( ).
|
 |
 |
 |
When you run the program, you’ll
notice that the value of i is retained between requests to the servlet.
This is an essential property of servlets: since only one servlet of a
particular class is loaded into the container, and it is never unloaded (unless
the servlet container is terminated, which is something that only normally
happens if you reboot the server computer), any fields of that servlet class
effectively become persistent objects! This means that you can effortlessly
maintain values between servlet requests, whereas with CGI you had to write
values to disk in order to preserve them, which required a fair amount of
fooling around to get it right, and resulted in a non-cross-platform
solution.
|
 |
En lançant le programme, on peut remarquer que la valeur de
i ne change pas entre les requêtes vers la servlet. C'est une propriété
essentielle des servlets : tant qu'il n'existe qu'une servlet d'une classe particulière
chargée dans le conteneur, et jamais déchargée (sauf en cas de fin du conteneur de servlet, ce qui
ne se produit normalement que si l'on reboote l'ordinateur serveur), tous les champs de cette
classe servlet sont des objets persistants ! Cela signifie que vous pouvez sans effort
supplémentaire garder des valeurs entre les requêtes à la servlet, alors qu'avec CGI vous auriez dû
écrire ces valeurs sur disque afin de les préserver, ce qui aurait demandé du temps supplémentaire
et fini par déboucher sur une solution qui n'aurait pas été inter-plate-forme.
|
 |
 |
 |
Of course, sometimes the Web server, and
thus the servlet container, must be rebooted as part of maintenance or during a
power failure. To avoid losing any persistent information, the servlet’s
init( ) and destroy( ) methods are automatically called
whenever the servlet is loaded or unloaded, giving you the opportunity to save
data during shutdown, and restore it after rebooting. The servlet container
calls the destroy( ) method as it is terminating itself, so you
always get an opportunity to save valuable data as long as the server machine is
configured in an intelligent way.
|
 |
Bien entendu, le serveur Web ainsi que le conteneur de servlet doivent de
temps en temps être rebootés pour des raisons de maintenance ou après une coupure de courant. Pour
éviter de perdre toute information persistante, les méthodes de servlet
init( ) et destroy( ) sont appelées automatiquement
chaque fois que la servlet est chargée ou déchargée, ce qui nous donne l'opportunité de sauver des
données lors d'un arrêt, puis de les restaurer après que la machine ait été rebootée. Le conteneur
de la servlet appelle la méthode destroy( ) lorsqu'il se termine lui-même, et
on a donc toujours une opportunité de sauver des données essentielles pour peu que la machine
serveur soit intelligemment configurée.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |
|
 |