3. Introdução aos servlets Java e às páginas JSP
Este capítulo contém vários exemplos de servlets e páginas JSP. Foram testados com o servidor Tomcat, que funciona na porta 8080. Seguindo as ligações na página inicial, pode aceder a exemplos de servlets e páginas JSP. A maioria dos exemplos abaixo foi retirada dos exemplos do Tomcat. Para os testar, basta iniciar o Tomcat, introduzir o URL http://localhost:8080 num navegador e seguir o link do servlet.
![]() | ![]() |
3.1. Servlets Java
3.1.1. Envio de conteúdo HTML para um cliente Web
Vamos analisar o exemplo «Hello World» acima. O servlet é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
Quando este servlet é executado, é exibido o seguinte:

Tenha em atenção os seguintes pontos:
- É necessário importar classes especiais para servlets:
import javax.servlet.*;
import javax.servlet.http.*;
A biblioteca javax.servlet nem sempre está incluída por predefinição no JDK. Nesse caso, pode descarregá-la diretamente do site da Sun.
- Um servlet estende a classe HttpServlet
public class HelloWorld extends HttpServlet {
- Uma solicitação GET feita ao servlet é tratada pelo método doGet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Da mesma forma, uma solicitação POST feita ao servlet é tratada pelo método doPost
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- O objeto de solicitação HttpServletRequest é o objeto que nos dá acesso à solicitação feita pelo cliente web. A resposta do servlet será enviada através do objeto de resposta HttpServletResponse
- O objeto response permite-nos definir os cabeçalhos HTTP que serão enviados ao cliente. Por exemplo, o cabeçalho Content-Type: text/html é definido aqui da seguinte forma:
- Para enviar a resposta ao cliente, o servlet utiliza um fluxo de saída fornecido pelo objeto response:
- Assim que este fluxo de saída é obtido, o código HTML é gravado nele e, assim, enviado ao cliente:
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
3.1.2. Recuperação de parâmetros enviados por um cliente web
O exemplo seguinte mostra como um servlet pode recuperar parâmetros enviados pelo cliente web. Um formulário de entrada:

A resposta enviada pelo servlet:

O código-fonte do servlet é o seguinte:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
Observe as seguintes alterações em relação ao exemplo anterior:
- Os parâmetros enviados pelo navegador são recuperados da seguinte forma:
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
O método request.getParameter("parameterName") devolve um ponteiro nulo se o parâmetro "parameterName" não estiver entre os parâmetros enviados pelo cliente web.
- O formulário especifica que o navegador deve enviar os parâmetros utilizando o método POST
- Os parâmetros recebidos serão processados pelo método doPost do servlet. Aqui, este método simplesmente chama o método doGet. Assim, este servlet processa os valores do formulário independentemente de serem enviados via GET ou POST.
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
3.1.3. Recuperação de cabeçalhos HTTP enviados por um cliente web
O servlet a seguir demonstra como recuperar os cabeçalhos HTTP enviados pelo cliente web:

O código-fonte do servlet é o seguinte:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestHeaderExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println(name + " = " + value);
}
}
}
Pontos a ter em conta:
- É o objeto `request` e o seu método `getHeaderNames` que nos dão acesso aos cabeçalhos HTTP enviados pelo navegador na forma de uma enumeração:
- O método request.getHeader("header") permite-lhe recuperar um cabeçalho HTTP específico. O exemplo acima apresenta alguns deles. Tenha em mente que os cabeçalhos aqui apresentados são enviados pelo navegador. O servidor também tem os seus próprios cabeçalhos HTTP, que por vezes espelham os do navegador. Os cabeçalhos HTTP enviados pelo navegador destinam-se a informar o servidor sobre as capacidades do navegador.
Cabeçalho | Significado |
Identidade do navegador | |
Tipos MIME aceites pelo navegador. Por exemplo, image/gif significa que o navegador consegue processar imagens no formato GIF | |
no formato host:porta. Indica a que máquina e porta o navegador pretende estabelecer contacto. | |
formato de codificação aceite pelo navegador para documentos enviados pelo servidor. Assim, se um servidor tiver um documento num formato normal, não comprimido, e outro no formato gzip, e o navegador tiver indicado que consegue lidar com o formato gzip, então o servidor pode enviar o documento no formato gzip para poupar largura de banda. | |
Idiomas aceites pelo navegador. Se um servidor tiver o mesmo documento em vários idiomas, enviará a versão no idioma aceite pelo navegador. | |
O URL solicitado pelo navegador | |
O modo de ligação solicitado pelo navegador. Keep-alive significa que o servidor não deve encerrar a ligação após enviar a página solicitada ao navegador. Se o navegador detetar que a página recebida contém ligações para imagens, por exemplo, pode efetuar novos pedidos ao servidor para as recuperar sem necessidade de estabelecer uma nova ligação. O navegador tomará então a iniciativa de encerrar a ligação assim que tiver recebido todos os elementos da página. |
3.1.4. Recuperação de informações do ambiente
O servlet a seguir demonstra como aceder às informações do ambiente de execução do servlet. Algumas dessas informações são enviadas como cabeçalhos HTTP pelo navegador e, portanto, podem ser recuperadas utilizando o método anterior.

O código do servlet é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestInfo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Request Information Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Request Information Example</h3>");
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
A informação é obtida aqui através de vários métodos:
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
Aqui está uma lista de alguns dos métodos disponíveis e os seus significados:
método | significado |
o nome do servidor web | |
a porta de trabalho do servidor web | |
o método GET ou POST utilizado pelo navegador para efetuar o seu pedido | |
o nome do computador do cliente a partir do qual o navegador efetuou o pedido | |
o endereço IP dessa mesma máquina | |
o tipo de conteúdo enviado pelo navegador (cabeçalho HTTP Content-Type) | |
o número de caracteres enviados pelo navegador (cabeçalho HTTP Content-Length) | |
a versão do protocolo HTTP solicitada pelo navegador | |
O URI solicitado pelo navegador. Corresponde à parte do URL que se segue ao identificador host:port em http://hote:port/URI |
3.1.5. Crie um servlet com o JBuilder e implemente-o com o Tomcat
Vamos agora descrever como criar e executar um servlet Java. Utilizaremos duas ferramentas: o JBuilder para compilar o servlet e o Tomcat para o executar. O Tomcat por si só pode ser suficiente. No entanto, oferece capacidades de depuração limitadas. Vamos revisitar o exemplo desenvolvido anteriormente que apresenta os parâmetros recebidos pelo servidor. O servlet envia primeiro o seguinte formulário de entrada:

A resposta enviada pelo servlet:

O código-fonte do servlet é o seguinte:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
- Crie um projeto chamado myRequestParamExample utilizando o JBuilder e inclua nele o programa myRequestParamExample.java anterior.
- Durante a compilação, poderá deparar-se com o seguinte problema: o seu JBuilder poderá não ter a biblioteca javax.servlet necessária para compilar servlets. Neste caso, terá de configurar o JBuilder para utilizar bibliotecas de classes adicionais. O procedimento está descrito nos apêndices deste documento para o JBuilder 7. Reproduzimos-o parcialmente aqui:
- Ative a opção Ferramentas/Configurar JDKs

Na secção «Configurações do JDK» acima, o campo «Nome» apresenta normalmente «JDK 1.3.1». Se tiver um JDK mais recente, utilize o botão «Alterar» para especificar o seu diretório de instalação. Acima, especificámos o diretório E:\Program Files\jdk14, onde foi instalado um JDK 1.4. A partir de agora, o JBuilder utilizará este JDK para as suas compilações e execuções. Na secção (Class, Source, Documentation), verá uma lista de todas as bibliotecas de classes que o JBuilder irá analisar — neste caso, as classes do JDK 1.4. As classes incluídas neste JDK não são suficientes para o desenvolvimento web em Java. Para adicionar bibliotecas de classes adicionais, utilize o botão Add e selecione os ficheiros .jar extra que pretende utilizar. Os ficheiros .jar são bibliotecas de classes. O Tomcat 4.x inclui todas as bibliotecas de classes necessárias para o desenvolvimento web. Estas encontram-se em <tomcat>\common\lib, onde <tomcat> é o diretório de instalação do Tomcat:

Usando o botão Adicionar, vamos adicionar estas bibliotecas, uma a uma, à lista de bibliotecas analisadas pelo JBuilder:

A partir de agora, pode compilar programas Java em conformidade com a norma J2EE, incluindo servlets Java. O JBuilder é utilizado apenas para a compilação; a execução é posteriormente tratada pelo Tomcat.
- Agora pode compilar o programa myRequestParamExample.java e gerar o servlet myRequestParamExample.class. Onde deve este servlet ser colocado? Se a configuração inicial do Tomcat não tiver sido alterada, os ficheiros .class do servlet devem ser colocados em <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
- Verifique se o Tomcat está a funcionar e utilize um navegador para aceder ao URL http://localhost:8080/examples/servlet/myRequestParamExample:

3.1.6. Exemplos
Para os exemplos a seguir, utilizámos o método descrito acima:
- compilar o ficheiro fonte XX.java do servlet com o JBuilder
- implementar o servlet XX.class em <tomcat>\webapps\examples\WEB-INF\classes
- Com o Tomcat em execução, introduza o URL http://localhost:8080/examples/servlet/XX num navegador
3.1.6.1. Geração dinâmica de formulários - 1
Usaremos como exemplo a geração de um formulário com apenas um controlo: uma lista. O conteúdo desta lista é construído dinamicamente utilizando valores retirados de uma matriz. Na realidade, estes valores são frequentemente recuperados de uma base de dados. O formulário é o seguinte:

Se clicar em Enviar no exemplo acima, obtém a seguinte resposta:

Note que o URL que devolve a resposta é o mesmo que o que apresenta o formulário. Aqui, temos um servlet que processa a resposta ao formulário que enviou. Este é um cenário comum. O código HTML do formulário é o seguinte:
<html>
<head><title>Génération de formulaire</title></head>
<body>
<h3>Choississez un nombre</h3><hr>
<form method="POST">
<select name="cmbValeurs" size="1">
<option>zéro</option>
<option>un</option>
<option>deux</option>
<option>trois</option>
<option>quatre</option>
<option>cinq</option>
<option>six</option>
<option>sept</option>
<option>huit</option>
<option>neuf</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Note que os valores enviados pelo formulário são enviados utilizando o método POST. O código HTML da resposta:
<html>
<head><title>Voici ma réponse</title></head>
<body>
Vous avez choisi le nombre<h2>neuf</h2>
</body>
</html>
O código do servlet que gera este formulário e esta resposta é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener1 extends HttpServlet{
// variables d'instance
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choississez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\">";
private final String HTML3="</form>\n</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
for (int i=0;i<valeurs.length;i++){
out.println("<option>"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// fin formulaire
out.println(HTML2+HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on récupère le choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) doGet(request,response);
// on prépare la réponse
String réponse="<html><head><title>Voici ma réponse</title></head>";
réponse+="<body>Vous avez choisi le nombre <h2>"+choix+"</h2></body></html>";
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
out.println(réponse);
}//POST
}//classe
O método doGet é utilizado para gerar o formulário. Existe uma parte dinâmica, que é o conteúdo da lista, obtido aqui a partir de um array. O método doPost é utilizado para gerar a resposta. Aqui, a única parte dinâmica é o valor da seleção do utilizador na lista do formulário. Este valor é obtido através de request.getParameter("cmbValeurs"), onde cmbValeurs é o nome HTML da lista:
Por fim, tenha em atenção os seguintes pontos:
- O navegador envia os valores do formulário para o servlet que o gerou, uma vez que a tag <form> não possui o atributo <action>. Neste caso, o navegador envia os dados introduzidos no formulário para o URL que o forneceu.
- A tag <form> especifica que os dados do formulário devem ser enviados utilizando o método POST. É por isso que estes valores são recuperados pelo método doPost do servlet.
3.1.6.2. Geração Dinâmica de Formulários - 2
Vamos revisitar o exemplo anterior e modificá-lo da seguinte forma. O formulário permanece o mesmo:

Mas a resposta é diferente:

Na resposta, o formulário é devolvido, com o número escolhido pelo utilizador indicado abaixo dele. Além disso, este número é o que aparece como selecionado quando a lista é apresentada. O utilizador pode então escolher outro número:

e clicar em Enviar. Recebe a seguinte resposta:

O código para o servlet denominado gener2.java é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener2 extends HttpServlet{
// variables d'instance
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// on indique au client le type de document envoyé
response.setContentType("text/html");
// on envoie le formulaire
PrintWriter out=response.getWriter();
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// suite formulaire
out.println(HTML2);
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// fin du formulaire
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on renvoie sur GET
doGet(request,response);
}//POST
}//classe
O método doGet faz tudo: constrói o formulário que envia ao cliente e processa os valores que o cliente devolve. Vale a pena destacar os seguintes pontos:
- Verificamos se o parâmetro cmbValeurs tem um valor.
- Se for o caso, ao criar o conteúdo da lista, cada item é comparado com a seleção do utilizador para definir o atributo selected no item escolhido pelo utilizador: <option selected>item</option>. Além disso, o valor selecionado é apresentado abaixo do formulário.
3.1.6.3. Geração Dinâmica de Formulários - 3
Estamos a abordar o mesmo problema de antes, mas desta vez os valores são recuperados de uma base de dados. No nosso exemplo, trata-se de uma base de dados MySQL:
- a base de dados chama-se dbValues
- o seu proprietário é admDbValeurs com a palavra-passe mdpDbValeurs
- a base de dados tem uma única tabela chamada tvaleurs
- esta tabela tem apenas um campo inteiro chamado value
E:\Program Files\EasyPHP\mysql\bin>mysql --database=dbValeurs --user=admDbValeurs --password=mdpDbVa
leurs
mysql> show tables;
+---------------------+
| Tables_in_dbValeurs |
+---------------------+
| tvaleurs |
+---------------------+
1 row in set (0.00 sec)
mysql> describe tvaleurs;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| valeur | int(11) | | | 0 | |
+--------+---------+------+-----+---------+-------+
mysql> select * from tvaleurs;
+--------+
| valeur |
+--------+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 6 |
| 5 |
| 7 |
| 8 |
| 9 |
+--------+
10 rows in set (0.00 sec)
A base de dados MySQL dbValeurs foi disponibilizada através de um controlador ODBC para MySQL. O seu DSN (Data Source Name) é odbc-valeurs. O código do servlet é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener3 extends HttpServlet{
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// valeurs de liste
private String[] valeurs=null;
// msg d'erreur
private String msgErreur=null;
// code HTML
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on indique au client le type de document envoyé
response.setContentType("text/html");
// flux de sortie
PrintWriter out=response.getWriter();
// l'initialisation de la servlet s'est-elle bien passée ?
if (msgErreur!=null){
// il y a eu une erreur - on génère une page d'erreur
out.println("<html><head><title>"+title+"</title></head>");
out.println("<body><h3>Application indisponible ("+msgErreur+
")</h3></body></html>");
return;
}//if
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// on envoie le formulaire
// début
out.println(HTML1);
// combo
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// suite formulaire
out.println(HTML2);
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//if
// fin du formulaire
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// on renvoie sur GET
doGet(request,response);
}//POST
// initialisation de la servlet
public void init(){
// remplit le tableau des valeurs à partir d'une base de données ODBC
// de nom DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connexion à la base ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// objet Statement
st=connexion.createStatement();
// exécution requête select pour récupérer les valeurs
rs=st.executeQuery("select valeur from Tvaleurs");
// les valeurs sont récupérées et mises dans un tableau dynamique
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// on enregistre la valeur dans la liste
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation liste --> tableau
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problème
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
}//classe
Os pontos-chave a ter em conta são os seguintes:
- Um servlet pode ser inicializado por um método cuja assinatura deve ser public void init(). Este método é executado apenas quando o servlet é carregado inicialmente
- Uma vez carregado, um servlet permanece na memória em todos os momentos. Isto significa que, após atender um cliente, ele não é descarregado. Assim, responde mais rapidamente às solicitações dos clientes.
- No nosso servlet, é necessário recuperar uma lista de valores de uma base de dados. Uma vez que esta lista não se altera ao longo do tempo, o método init é o momento ideal para a recuperar. A base de dados é, assim, acedida apenas uma vez pelo servlet, quando este é carregado inicialmente, em vez de ser acedida a cada pedido do cliente.
- O acesso à base de dados pode falhar. O método init do nosso servlet define uma mensagem de erro, msgErreur, em caso de falha. Esta mensagem é verificada no método doGet e, se ocorrer um erro, o doGet gera uma página a indicá-lo.
- A implementação do método init utiliza o acesso padrão à base de dados com controladores ODBC-JDBC. Se necessário, o leitor é encorajado a rever os métodos para aceder a bases de dados JDBC.
Quando o servlet é executado e o servidor MySQL não foi iniciado, aparece a seguinte página de erro:
![]()
Se iniciar agora o servidor MySQL, verá a página:

Se selecionar o número 6 e clicar em «Submit»:

3.1.6.4. Recuperar valores de um formulário
Vamos revisitar um exemplo que já vimos, o do seguinte formulário web:

O código HTML do formulário balises2.htm é o seguinte:
<html>
<head>
<title>balises</title>
<script language="JavaScript">
function effacer(){
alert("Vous avez cliqué sur le bouton Effacer");
}//effacer
</script>
</head>
<body background="/images/standard.jpg">
...
<form method="POST" action="http://localhost:8080/examples/servlet/parameters">
<table border="0">
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" value="Oui" name="R1">Oui
<input type="radio" name="R1" value="non" checked>Non
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="C1" value="un">1
<input type="checkbox" name="C2" value="deux" checked>2
<input type="checkbox" name="C3" value="trois">3
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="txtSaisie" size="20" value="qqs mots">
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="txtMdp" size="20" value="unMotDePasse">
</td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td>
<textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
</td>
</tr>
<tr>
<td>combo</td>
<td>
<select size="1" name="cmbValeurs">
<option>choix1</option>
<option selected>choix2</option>
<option>choix3</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix simple</td>
<td>
<select size="3" name="lst1">
<option selected>liste1</option>
<option>liste2</option>
<option>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix multiple</td>
<td>
<select size="3" name="lst2" multiple>
<option selected>liste1</option>
<option>liste2</option>
<option selected>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>bouton</td>
<td>
<input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
</td>
</tr>
<tr>
<td>envoyer</td>
<td>
<input type="submit" value="Envoyer" name="cmdRenvoyer">
</td>
</tr>
<tr>
<td>rétablir</td>
<td>
<input type="reset" value="Rétablir" name="cmdRétablir">
</td>
</tr>
</table>
<input type="hidden" name="secret" value="uneValeur">
</form>
</body>
</html>
A tag <form> no formulário foi definida da seguinte forma:
O navegador irá «enviar» os valores do formulário para o URL http://localhost:8080/examples/servlet/parameters, que é o URL de um servlet gerido pelo Tomcat e que apresenta os valores do formulário anterior. Se chamarmos o servlet de parâmetros diretamente, obtemos os seguintes resultados:

Se o formulário balises2.htm introduzido for este:

e clicar no botão «Submit», o servlet «parameters» é chamado com os parâmetros desta vez. Em seguida, devolve a seguinte resposta:

Esta resposta contém claramente os valores introduzidos no formulário. O código do servlet é o seguinte:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class parameters extends HttpServlet{
// variables d'instance
String title="Récupération des paramètres d'un formulaire";
private String getParameter(HttpServletRequest request, String contrôle){
// rend la valeur request.getParameter(contrôle) ou "" si elle n'existe pas
String valeur=request.getParameter(contrôle);
if(valeur==null) return ""; else return valeur;
}//getParameter
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// on commence par récupérer les paramètres du formulaire
String R1=getParameter(request,"R1");
String C1=getParameter(request,"C1");
String C2=getParameter(request,"C2");
String C3=getParameter(request,"C3");
String txtSaisie=getParameter(request,"txtSaisie");
String txtMdp=getParameter(request,"txtMdp");
String areaSaisie=getParameter(request,"areaSaisie");
String[] lignes=areaSaisie.split("\\r\\n");
String cmbValeurs=getParameter(request,"cmbValeurs");
String lst1=getParameter(request,"lst1");
String[] lst2=request.getParameterValues("lst2");
String secret=getParameter(request,"secret");
// on indique le contenu du document
response.setContentType("text/html");
// on envoie le document
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
out.println("<hr>");
out.println("<table border=\"1\">");
out.println("<tr><td>R1</td><td>"+R1+"</td></tr>");
out.println("<tr><td>C1</td><td>"+C1+"</td></tr>");
out.println("<tr><td>C2</td><td>"+C2+"</td></tr>");
out.println("<tr><td>C3</td><td>"+C3+"</td></tr>");
out.println("<tr><td>txtSaisie</td><td>"+txtSaisie+"</td></tr>");
out.println("<tr><td>txtMdp</td><td>"+txtMdp+"</td></tr>");
for(int i=0;i<lignes.length;i++)
out.println("<tr><td>areaSaisie["+i+"]</td><td>"+lignes[i]+"</td></tr>");
out.println("<tr><td>cmbValeurs</td><td>"+cmbValeurs+"</td></tr>");
out.println("<tr><td>lst1</td><td>"+lst1+"</td></tr>");
if(lst2==null)
out.println("<tr><td>lst2</td><td></td></tr>");
else
for(int i=0;i<lst2.length;i++)
out.println("<tr><td>lst2</td><td>"+lst2[i]+"</td></tr>");
out.println("<tr><td>secret</td><td>"+secret+"</td></tr>");
out.println("</body>");
out.println("</html>");
}
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// renvoie sur GET
doGet(request,response);
}
}
Este código utiliza as técnicas apresentadas anteriormente noutro exemplo. Tenha em atenção os dois pontos seguintes:
- O controlo lst2 é uma lista de seleção múltipla, pelo que é possível selecionar vários itens. É o que acontece no nosso exemplo, em que os itens list1 e list3 foram selecionados. Os valores de lst2 foram enviados pelo navegador para o servidor na forma lst2=liste1&lst2=liste3. O servlet Java pode recuperar esses valores numa matriz utilizando o método getParameterValues: aqui, request.getParameterValues("lst2") devolve uma matriz de duas cadeias de caracteres ["liste1", "liste3"].
- O controlo areaSaisie é um campo de entrada multilinha. request.getParameter("areaSaisie") devolve o conteúdo do campo como uma única string. Se pretender extrair as linhas individuais dentro dele, pode utilizar o método split da classe String. O código seguinte
recupera as linhas do campo de entrada. Estas linhas são terminadas pelos caracteres \r\n (0D0A).
Para realizar os testes, temos:
- criámos e compilámos os parâmetros do servlet utilizando o JBuilder, conforme explicado anteriormente
- colocado a classe gerada em <tomcat>\webapps\examples\WEB-INF\classes, onde <tomcat> é o diretório de instalação do Tomcat.
- solicitámos o URL http://localhost:81/html/balises2.htm, cujo código foi apresentado acima
- preenchemos o formulário e clicámos no botão Enviar.
3.1.6.5. Recuperação de cabeçalhos HTTP de um cliente web
Vamos usar o mesmo exemplo de antes, mas, em resposta ao cliente web que enviou os valores do formulário, vamos reenviar os cabeçalhos HTTP que ele enviou ao mesmo tempo. Vamos fazer uma única alteração no nosso formulário:
Os valores do formulário serão enviados utilizando o método GET para um servlet Java chamado headers, localizado em <tomcat>\webapps\examples\WEB-INF\classes. O servlet headers foi criado e compilado com o JBuilder:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class headers extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
// determine the nature of the document
response.setContentType("text/html");
// we obtain a writing flow
PrintWriter out = response.getWriter();
// display header list HTTP
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println("<b>"+name + "</b> = " + value + "<br>");
}
}//GET
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
//GET
doGet(request,response);
}//POST
}
Acedemos à URL http://localhost:81/html/balises2.htm e clicamos em «Submeter» sem alterar o formulário. Recebemos a seguinte resposta:

Repare na URL parametrizada na barra de endereços do navegador, que mostra o método (GET) utilizado para transmitir os parâmetros. Vamos utilizar o mesmo exemplo, mas alterar o método de envio dos parâmetros (POST):
Recebemos a seguinte resposta nova:

Repare nos cabeçalhos HTTP content-type e content-length, que são característicos de um pedido POST. Além disso, repare que os valores do formulário já não aparecem no campo de endereço do navegador.
3.2. Páginas JSP
As JSP (Java Server Pages) são outra forma de escrever aplicações para servidores web. Na verdade, estas páginas JSP são traduzidas em servlets antes de serem executadas, pelo que estamos essencialmente a lidar com tecnologia de servlets. As páginas JSP permitem destacar de forma mais clara a estrutura das páginas HTML geradas. Seguem-se alguns exemplos, alguns dos quais podem ser acedidos seguindo a ligação JSP na página inicial do Tomcat:
![]() | ![]() |
3.2.1. Recuperação de informações do ambiente
Aqui, retomamos um exemplo anteriormente abordado com um servlet: a exibição das variáveis de ambiente de um servlet. Este é o exemplo “snoop” dos exemplos de JSP:

O código-fonte da página JSP encontra-se em <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) ou <tomcat>\examples\jsp\snp\snoop.jsp (Tomcat 4.x)
<html>
<!--
Copyright (c) 1999 The Apache Software Foundation. All rights
reserved.
-->
<body bgcolor="white">
<h1> Request Information </h1>
<font size="4">
JSP Request Method: <%= request.getMethod() %>
<br>
Request URI: <%= request.getRequestURI() %>
<br>
Request Protocol: <%= request.getProtocol() %>
<br>
Servlet path: <%= request.getServletPath() %>
<br>
Path info: <%= request.getPathInfo() %>
<br>
Path translated: <%= request.getPathTranslated() %>
<br>
Query string: <%= request.getQueryString() %>
<br>
Content length: <%= request.getContentLength() %>
<br>
Content type: <%= request.getContentType() %>
<br>
Server name: <%= request.getServerName() %>
<br>
Server port: <%= request.getServerPort() %>
<br>
Remote user: <%= request.getRemoteUser() %>
<br>
Remote address: <%= request.getRemoteAddr() %>
<br>
Remote host: <%= request.getRemoteHost() %>
<br>
Authorization scheme: <%= request.getAuthType() %>
<hr>
The browser you are using is <%= request.getHeader("User-Agent") %>
<hr>
</font>
</body>
</html>
Tenha em atenção os seguintes pontos:
- Este código assemelha-se muito ao HTML. No entanto, contém tags <%= expressão %> que são específicas da linguagem JSP. O compilador JSP substitui a tag inteira no texto HTML pelo valor da expressão.
- Este exemplo utiliza métodos do objeto Java `request`, que é o objeto `request` já encontrado no estudo dos servlets. Trata-se, portanto, de um objeto `HttpServletRequest`. Assim, a tag `<%= request.getRemoteHost() %>` será substituída no código HTML pelo nome da máquina do cliente web que efetuou o pedido.
- É possível obter o mesmo resultado com um servlet, mas aqui a estrutura da página web é mais evidente.
3.2.2. Recuperação de parâmetros enviados pelo cliente web
Aqui, revisamos o exemplo que estudámos anteriormente com um servlet. É apresentado um formulário ao navegador:

Em resposta ao pedido acima, o navegador recebe a seguinte página:

O código da página JSP é o seguinte:
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- code HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
- Embora voltemos a ver a tag <%= expressão %>, tal como no exemplo anterior, surge uma nova tag: <% instruções Java; %>. A tag <% introduz código Java. Este código termina quando encontra a tag de encerramento %>.
- Todo o código anterior (HTML + JSP) será convertido num servlet Java. Será incluído num único método, conhecido como o método principal da página JSP. É por isso que as variáveis Java declaradas no início da página JSP são acessíveis nas outras partes do código JSP intercaladas ao longo do HTML: estas variáveis e segmentos de código farão parte do mesmo método Java. No entanto, se o nosso código JSP contivesse métodos, as variáveis `title`, `firstname` e `lastname` não estariam acessíveis dentro deles devido ao escopo dos métodos. Teríamos de torná-las variáveis globais ou passá-las como parâmetros para os métodos. Voltaremos a este assunto mais tarde.
- Para incluir partes dinâmicas no código HTML, existem dois métodos possíveis: <%= expressão %> ou out.println(expressão). O objeto out é um fluxo de saída semelhante ao de mesmo nome encontrado nos exemplos de servlet, mas não do mesmo tipo: é um objeto JspWriter, não um PrintWriter. Permite escrever no fluxo HTML utilizando os métodos print e println.
- A página JSP reflete melhor a estrutura da página HTML gerada do que o servlet equivalente.
3.2.3. Etiquetas JSP
Aqui está uma lista de tags que poderá encontrar numa página JSP e os seus significados.
tag | Significado |
Comentário HTML. Enviado para o cliente. | |
Comentário JSP. Não enviado ao cliente. | |
Declara variáveis e métodos globais. As variáveis estarão disponíveis em todos os métodos | |
O valor da expressão será inserido na página HTML no lugar da tag | |
contém código Java que fará parte do método principal da página JSP | |
define atributos para a página JSP. Por exemplo: import="java.util.*,java.sql.*" para especificar as bibliotecas necessárias à página JSP extends="aParentClass" para que a página JSP herde de outra classe |
3.2.4. Objetos implícitos do JSP
Nos exemplos anteriores, deparámo-nos com dois objetos não declarados: request e out. Estes são dois dos objetos que são automaticamente definidos no servlet para o qual a página JSP é convertida. São chamados de objetos implícitos ou predefinidos. Existem outros, mas estes são os mais utilizados juntamente com o objeto response:
object | significado |
o objeto através do qual pode aceder ao pedido do cliente web (getParameter, getParameterNames, getParameterValues) | |
O objeto utilizado para construir a resposta do servidor web ao cliente. Permite definir os cabeçalhos HTTP a serem enviados ao cliente web. | |
o fluxo de saída que nos permite enviar código HTML para o cliente (print, println) |
3.2.5. Converter uma página JSP num servlet
Vamos rever o código JSP do myRequestParamExample.jsp:
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- code HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
Quando o navegador solicita esta página JSP ao servidor Tomcat, o servidor irá convertê-la num servlet. Se o URL solicitado for
http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp, o Tomcat 4.x colocará o servlet gerado no diretório <tomcat>\work\localhost\examples\jsp\perso\intro:

Este nome reflete a URL da página JSP, http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp. Conforme mostrado acima, podemos aceder ao código Java do servlet gerado para a página JSP. No nosso exemplo, é o seguinte:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class myRequestParamExample$jsp extends HttpJspBase {
static {
}
public myRequestParamExample$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// variables local to the main procedure
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
out.write("\r\n\r\n<!-- code HTML -->\r\n<html>\r\n <head>\r\n <title>");
out.print( title );
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
out.print( title );
out.write("</h3>\r\n ");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.write("\r\n <P>\r\n <form method=\"POST\">\r\n firstname= <input type=\"text\" size=\"20\" name=\"firstname\">\r\n <br>\r\n lastname= <input type=\"text\" size=\"20\" name=\"lastname\">\r\n <br>\r\n <input type=\"submit\">\r\n </form>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
O código gerado é bastante complexo. Vamos concentrar-nos apenas nos seguintes pontos:
- O método main do servlet é o seguinte:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
Este método é chamado quando o servlet é iniciado. Podemos ver que ele recebe dois parâmetros: o pedido do cliente e um objeto de resposta para gerar a sua resposta ao cliente web.
- No método main, é declarado e, em seguida, inicializado um objeto JspWriter denominado out. Este objeto é utilizado para enviar código HTML ao cliente através da instrução out.print("código HTML").
- O código Java
<%
// variables locales à la procédure principale
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
foi incluído na íntegra no método principal _jspService do servlet. O mesmo se aplica a qualquer código localizado dentro das tags <%… %>
- O código HTML da página JSP é gerado utilizando as instruções out.print("codeHTML") ou out.write(...). Por exemplo
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
- Neste exemplo, não existem outros métodos além do método principal _jspService.
3.2.6. Métodos e variáveis globais de uma página JSP
Considere a seguinte página JSP:
<%!
// la balise précédente démarre la partie variables et méthodes globales
// cette partie sera reprise sans modification dans la servlet
// une variabe globale
String prenom="inconnu";
// une méthode
private String sonChien(){
return "milou";
}//sonChien
// une autre méthode
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// fin de la partie globale de la servlet
%>
<%
// la balise précédente indique que le code qui suit sera enregistré
// dans la méthode principale de la servlet
// variable locale à la méthode principale
String nom="tintin";
%>
<%-- code HTML --%>
<html>
<head>
<title>Page JSP</title>
</head>
<body>
<center>
<h2>Page JSP</h2>
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// le nom de son ami
afficheAmi(out);
%>
</center>
</body>
</html>
Esta página JSP gera a seguinte página web:

Vamos ver como as quatro linhas acima são geradas:
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// le nom de son ami
afficheAmi(out);
%>
As linhas acima estão dentro de uma tag <%..%> e, portanto, farão parte do método principal _jspService do servlet que será gerado. Como é que elas acedem às variáveis lastName, firstName e aos métodos hisDog e displayFriend?
é uma variável local do método principal da página JSP e, portanto, é reconhecida dentro dela | |
é uma variável global da página JSP e, portanto, é reconhecida no método principal | |
é um método público da página JSP e, portanto, acessível a partir do método principal | |
é um método público da página JSP e, portanto, acessível a partir do método principal. Note-se que o objeto out é passado como parâmetro para o método. Isto é obrigatório aqui. Na verdade, o objeto out é declarado e inicializado no método principal do servlet e não é uma variável global. |
Vejamos agora o código do servlet Java gerado a partir desta página JSP, depois de removido o código desnecessário:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class tintin$jsp extends HttpJspBase {
// the preceding tag starts the global variables and methods section
// this part will be adopted unchanged in the servlet
// global variability
String prenom="inconnu";
// a method
private String sonChien(){
return "milou";
}//sonChien
// another method
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// end of the global part of the servlet
static {
}
public tintin$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
out.write(" \r\n\r\n");
// the preceding tag indicates that the following code will be saved
// in the servlet's main method
// variable local to the main method
String nom="tintin";
out.write("\r\n\r\n\r\n");
out.write("\r\n<html>\r\n <head>\r\n <title>Page JSP</title>\r\n </head>\r\n <body>\r\n <center>\r\n <h2>Page JSP</h2>\r\n <p>Son nom est ");
out.print( nom );
out.write("</p>\r\n <p>Son prénom est ");
out.print( prenom );
out.write("</p>\r\n <p>Son chien s'appelle ");
out.print( sonChien() );
out.write("</p>\r\n ");
// his friend's name
afficheAmi(out);
out.write("\r\n </center>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
Como mostrado acima, o código Java que se encontrava entre as tags JSP <%! .. %> foi incluído na íntegra e não faz parte do método principal do servlet, _jspService. As variáveis declaradas nesta secção são variáveis de instância e, portanto, globais para os métodos; é também aqui que podem ser definidos outros métodos além do _jspService.
// this part will be adopted unchanged in the servlet
// global variability
String prenom="inconnu";
// a method
private String sonChien(){
return "milou";
}//sonChien
// another method
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// end of the global part of the servlet
3.2.7. Implantação e depuração de páginas JSP no servidor Tomcat
Quando se pretende criar uma página JSP e utilizá-la com o servidor Tomcat, surge a questão de onde colocar a página na estrutura de diretórios do servidor. Existem diferentes formas de o fazer, às quais voltaremos mais tarde. Por enquanto, a abordagem mais simples é colocar a página JSP numa pasta dentro da árvore de diretórios <tomcat>\webapps\examples\jsp (Tomcat 4.x), onde <tomcat> é o diretório de instalação do Tomcat. Assim, o URL no exemplo anterior era http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Isto significa que a página tintin.jsp estava localizada na pasta <tomcat>\webapps\examples\jsp\perso\tintin.
Uma página JSP é traduzida num ficheiro fonte Java, que é então compilado pelo Tomcat quando a URL da página JSP é solicitada por um navegador. Podem ocorrer erros de compilação. O Tomcat 4.x reporta-os na sua resposta ao navegador. Indica especificamente quais as linhas do ficheiro .java que contêm erros. Os erros podem ter várias causas:
- o código JSP na página está incorreto (por exemplo, erros nas tags JSP utilizadas)
- o código Java incluído na página JSP está incorreto
A primeira causa pode ser eliminada verificando o código JSP da página. A segunda pode ser eliminada verificando o código Java. Isto pode ser feito compilando diretamente o ficheiro .java gerado para a página JSP utilizando uma ferramenta como o JBuilder, que oferece capacidades de depuração mais avançadas do que o Tomcat.
3.2.8. Exemplos
Vamos retomar o exemplo que abordámos anteriormente, que envolve um servlet em que um utilizador seleciona um número de uma lista e o servidor indica ao utilizador qual o número que selecionou, devolvendo a mesma lista com o elemento escolhido pelo utilizador destacado:

Para criar esta página, recuperámos o código do servlet e modificámo-lo da seguinte forma:
- mantivemos o código Java que não gerava código HTML tal como estava
- O código Java que gerava código HTML foi convertido numa mistura de código HTML e JSP
Isto resulta na seguinte página JSP:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// variables globales de l'application
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// valeurs de liste
private String[] valeurs=null;
// msg d'erreur
private String msgErreur=null;
// initialisation de la page JSP - n'est exécutée qu'une seule fois
public void jspInit(){
// remplit le tableau des valeurs à partir d'une base de données ODBC
// de nom DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connexion à la base ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// objet Statement
st=connexion.createStatement();
// exécution requête select pour récupérer les valeurs
rs=st.executeQuery("select valeur from Tvaleurs");
// les valeurs sont récupérées et mises dans un tableau dynamique
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// on enregistre la valeur dans la liste
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation liste --> tableau
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problème
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
%>
<%
// code de _jspService exécuté à chaque requête cliente
// y-at-il eu une erreur lors de l'initialisation de la page JSP ?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %></h3>
</body>
</html>
<%
// fin de jspService
return;
}//if
// on récupère l'éventuel choix de l'utilisateur
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
%>
<%-- pas d'erreur - code HTML de la page normale --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST">
<select name="cmbValeurs">
<%
// affichage dynamique des valeurs
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// y-avait-il une valeur choisie ?
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Tenha em atenção os seguintes pontos:
- as instruções de importação do servlet estão incluídas numa diretiva <% page import="..." %>
- A tag <%! ... %> engloba as variáveis globais da aplicação e os métodos Java
- O método init do servlet, que é executado apenas uma vez quando o servlet é carregado, é chamado jspInit para uma página JSP. Estes dois métodos têm a mesma finalidade. Por conseguinte, o código do método init do servlet foi incluído aqui na íntegra.
- As variáveis de instância do servlet — aquelas que devem estar acessíveis em vários métodos — foram incluídas na íntegra. Estas são principalmente as variáveis title, values e msgError, que são posteriormente utilizadas no código JSP.
- As tags <% ... %> delimitam o código Java que será incluído no método _jspService, executado quando um cliente faz uma solicitação.
- Tal como no servlet, o método _jspService verificará primeiro o valor da variável msgError para determinar se precisa de gerar uma página de erro. Se houver um erro, gera a página de erro e sai (return).
- Se não houver erro, gera o formulário com a lista de valores
- Depois de fazer isso, verifica se o utilizador selecionou um número; se sim, exibe esse número na página gerada
Quais são as vantagens em relação a um servlet? Sem dúvida, uma visão mais clara do código HTML gerado. Mas ainda há muito código Java que «sobrecarrega» essa visão. Veremos mais tarde outro método chamado delegação, onde podemos colocar a maior parte do código Java num servlet, com a página JSP a reter apenas o código HTML e JSP. Isto separa claramente a parte de processamento da parte de apresentação.
3.3. Implementação de uma aplicação Web no servidor Tomcat
Vamos agora explicar como implementar aplicações Web Java utilizando o servidor Tomcat. Embora as instruções seguintes sejam específicas para este servidor, a implementação de uma aplicação Web Java noutro contentor J2EE envolverá passos semelhantes aos aqui descritos.
3.3.1. Os ficheiros de configuração server.xml e web.xml
Até agora, para testar os nossos servlets e páginas JSP, colocámos
- os servlets na pasta <tomcat>\webapps\examples\WEB-INF\classes. Estavam então acessíveis através do URL http://localhost:8080/examples/servlet/nomServlet
- as páginas JSP na árvore de diretórios <tomcat>\webapps\examples\jsp. Elas ficavam então acessíveis através da URL http://localhost:8080/examples/jsp/nomPageJSP
Nunca explicámos por que razão isto acontecia. O servidor Tomcat é configurado num ficheiro de texto chamado server.xml, localizado no diretório <tomcat>\conf:

Este ficheiro de texto é, na verdade, um ficheiro XML (eXtended Markup Language). Um documento XML é um documento de texto que contém tags, tal como um documento HTML. No entanto, enquanto as tags HTML são bem definidas, as tags XML não o são. Assim, o documento seguinte é um documento XML:
Um documento XML é simplesmente um documento «marcado» que segue certas regras de marcação:
- um texto marcado na forma <xx att1="val1" att2="val2" ....>texto</xx>
- uma tag pode ser independente e assumir a forma <xx att1="val1" att2="val2" ..../>
Os campos «att» são chamados atributos da tag «xx», e os campos «val» são os valores associados a esses atributos. Alguns documentos HTML não são documentos XML válidos. Por exemplo, a tag HTML <br> não é uma tag XML válida. Deve ser escrita como <br/> para ser válida, a fim de cumprir a regra de que todas as tags XML devem ser fechadas. Foi criada uma variante do HTML chamada XHTML para garantir que todos os documentos XHTML sejam documentos XML válidos. Alguns navegadores modernos são capazes de exibir ficheiros XML. Assim, se chamarmos ao documento XML apresentado no exemplo acima «person.xml» e o visualizarmos com o IE6, obtemos a seguinte exibição:

O IE6 reconhece as tags e destaca-as. Também reconhece a estrutura do documento com base nas tags. Assim, se chamarmos ao documento seguinte “person2.xml”:
e o visualizarmos com o IE6, obtemos a mesma apresentação:

O IE6 reconheceu corretamente a estrutura e o conteúdo do documento. O objetivo principal de um documento XML reside nesta propriedade: é fácil recuperar a estrutura e o conteúdo de um documento XML. Isto é feito utilizando um programa chamado analisador XML. Os documentos XML estão a tornar-se o padrão para a troca de documentos na Web. Consideremos a máquina A, que precisa de enviar um documento DOC para a máquina B. O documento DOC é construído a partir de informações contidas na base de dados DB-A. A máquina B deve armazenar o documento DOC na base de dados DB-B. A troca pode ocorrer da seguinte forma:
- A máquina A recupera os dados da base de dados DB-A e encapsula-os num documento de texto XML
- o documento XML é enviado para a máquina B através da rede
- A máquina B analisa o documento recebido com um analisador XML e extrai tanto a estrutura como os dados (tal como o IE6 fez no nosso exemplo). Pode então armazenar os dados recebidos na base de dados DB-B
Não falaremos mais sobre a linguagem XML, que merece um livro inteiro só para ela.
Aqui, então, o Tomcat é configurado pelo ficheiro XML server.xml. Se visualizarmos este ficheiro com o IE6, obtemos um documento complexo. Vamos concentrar-nos simplesmente nas seguintes linhas:

É a tag <Context ...> que nos interessa aqui. É utilizada para definir aplicações web. Dois dos seus atributos merecem destaque:
- path: este é o nome da aplicação web
- docBase: esta é a pasta na qual se encontra. Aqui, trata-se de um nome relativo: examples. Relativo a que pasta? A resposta também se encontra no ficheiro server.xml, na seguinte linha:

A linha acima define o servidor web:
- name: nome do servidor web
- appBase: raiz da árvore de documentos que ele serve. Mais uma vez, temos um nome relativo: webapps. É relativo ao diretório de instalação do servidor Tomcat <tomcat>. Assim, isto refere-se à pasta <tomcat>\webapps.
A aplicação web de exemplos armazena os seus ficheiros na pasta «examples» (ver «docBase» acima). Este nome é relativo à raiz da árvore de diretórios web do servidor, ou seja, <tomcat>\webapps. Trata-se, portanto, da pasta <tomcat>\webapps\examples. Vamos analisar esta pasta mais detalhadamente:

Aí encontramos a pasta WEB-INF\classes, onde armazenámos os nossos servlets para teste. A pasta WEB-INF contém um ficheiro chamado web.xml:

Este ficheiro é utilizado para configurar a aplicação web de exemplo. Não vamos entrar em detalhes sobre este ficheiro, que é demasiado complexo para o momento. Vamos simplesmente concentrar-nos nas seguintes linhas:
<servlet>
<servlet-name>
servletToJsp
</servlet-name>
<servlet-class>
servletToJsp
</servlet-class>
</servlet>
A tag <servlet> é utilizada para definir um servlet dentro de uma aplicação web. Note-se que a aplicação web em questão é a examples. A tag servlet contém duas outras tags:
- <servlet-name>servletToJsp</servlet-name>: define o nome do servlet
- <servlet-name>servletToJsp</servlet-name>: define o nome da classe a ser executada quando o servlet for solicitado. Neste exemplo, o servlet e a sua classe têm o mesmo nome. Isto não é obrigatório.
Como é que o servlet servletToJsp é solicitado por um navegador ao servidor Tomcat?
- O navegador solicita a URL http://localhost:8080/examples/servlet/servletToJsp
- O Tomcat analisa o caminho do servlet /examples/servlet/servletToJsp. Ele interpreta a primeira parte do caminho /examples como o nome de uma aplicação web e consulta o seu ficheiro de configuração server.xml para verificar onde os documentos desta aplicação estão armazenados. Como vimos anteriormente, isto encontra-se na pasta <tomcat>\webapps\examples.
- O Tomcat utiliza o resto do caminho do servlet para o localizar dentro da aplicação web examples. Este caminho /servlet/servletToJsp indica que deve executar o servlet denominado servletToJsp. O Tomcat irá então ler o ficheiro de configuração web.xml da aplicação examples, que encontrará em <tomcat>\webapps\examples\WEB-INF. Encontrará neste ficheiro que o servlet servletToJsp está ligado à classe Java servletToJsp (ver o ficheiro web.xml acima). Em seguida, procurará esta classe na pasta WEB-INF\classes da aplicação web de exemplos, ou seja, em <tomcat>\webapps\examples\WEB-INF\classes, e executá-la-á.

3.3.2. Exemplo: Implementação da aplicação web "list"
Vamos revisitar um servlet que já estudámos, que apresentava ao utilizador uma lista de números a partir da qual este selecionava um. O servlet confirmava então o número que tinha sido escolhido:

Conforme mostrado no campo de endereço do navegador acima, o ficheiro de classe do servlet foi denominado gener3. Com base nas explicações fornecidas anteriormente:
- a URL /examples/servlet/gener3 indica que se trata de um servlet chamado gener3 da aplicação web de exemplos
- no ficheiro web.xml da aplicação de exemplos, não há nada que mencione um servlet chamado gener3. Como é que o Tomcat o encontrou, então? Depois de ter revisto todo o ficheiro web.xml, não consigo responder com certeza... A questão permanece...
Optamos por implementar o servlet gener3.class com o nome lstValeurs numa aplicação web chamada liste, localizada na pasta E:\data\serge\Servlets\lstValeurs:

Colocamos o ficheiro gener3.class na pasta WEB-INF\classes acima mencionada:

Configuramos a aplicação web liste adicionando as seguintes linhas ao ficheiro server.xml, acima das que definem a aplicação web manager:
<!-- Perso: lstValeurs -->
<Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />
<!-- Tomcat Manager Context -->
<Context path="/manager" docBase="manager" debug="0" privileged="true" />
<!-- Tomcat Examples Context -->
<Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........
A linha que define a aplicação lstValeurs indica que esta se encontra na pasta e:/data/serge/servlets/lstValeurs. Temos agora de definir o ficheiro web.xml para esta aplicação. Este ficheiro irá definir o único servlet da aplicação:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
</web-app>
O ficheiro acima indica que o servlet denominado lstValeurs está associado ao ficheiro de classe gener3.class. Este ficheiro web.xml deve ser criado e guardado na pasta WEB-INF da aplicação liste:

A captura de ecrã acima mostra uma pasta src na qual foi colocado o ficheiro fonte gener3.java. Esta pasta pode não existir. Não tem qualquer utilidade nesta demonstração. Estamos prontos para executar os testes:
- Pare e reinicie o Tomcat para que ele volte a ler o seu ficheiro de configuração server.xml. Aqui estamos a utilizar o Windows. No Unix, pode forçar o Tomcat a voltar a ler o seu ficheiro de configuração sem o parar.
- Utilizando um navegador, aceda ao URL http://localhost:8080/liste/servlet/lstValeurs

Podemos ver que a URL anterior contém a palavra-chave *servlet*, tal como todas as URLs de servlet utilizadas até agora. Podemos eliminar isto associando o servlet lstValeurs a um padrão de URL (*url-pattern*) no ficheiro web.xml da aplicação:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
Na tag <servlet-mapping>, associamos o caminho /values ao servlet lstValeurs definido nas linhas anteriores. Guardamos o novo ficheiro web.xml e acedemos à URL http://localhost:8080/liste/valeurs:

3.3.3. Implantação das páginas públicas de uma aplicação web
Acabámos de ver como implementar uma aplicação web composta por um único servlet. Uma aplicação web pode ter muitos componentes: servlets, páginas JSP, ficheiros HTML, applets Java, etc. Onde colocamos estes elementos da aplicação? Se <application> é o diretório da aplicação web definido pelo atributo docBase da aplicação no ficheiro server.xml da configuração do Tomcat ( ), vimos que os servlets são colocados em <application>\WEB-INF\classes. Os outros componentes da aplicação podem ser colocados em qualquer local dentro da árvore de diretórios <application>, exceto no diretório WEB-INF. Considere a aplicação JSP listvaleurs.jsp que estudámos anteriormente:

Esta página JSP estava armazenada na pasta <tomcat>\webapps\examples\jsp\perso\listvaleurs. Esta página poderia ser um componente da aplicação list previamente implementada. Vamos colocar o ficheiro listvaleurs.jsp diretamente na pasta desta aplicação:

Lembre-se da configuração da aplicação liste no ficheiro server.xml:
Qualquer URL que comece com o caminho /liste é considerada parte da aplicação liste e será procurada na pasta especificada. Vamos aceder à URL http://localhost:8080/liste/listvaleurs.jsp utilizando um navegador:

De facto, obtivemos a página JSP esperada.
3.3.4. Parâmetros de inicialização do Servlet
Vimos que um servlet é configurado pelo ficheiro <application>\WEB-INF\web.xml, em que <application> é a pasta da aplicação web à qual pertence. É possível incluir parâmetros de inicialização do servlet neste ficheiro. Voltemos ao nosso servlet lstValeurs da aplicação web liste, cujo ficheiro de configuração era o seguinte:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
A classe associada ao servlet é a classe gener3. O código-fonte desta classe contém a definição de várias constantes:
public class gener3 extends HttpServlet{
// page title
private final String title="Génération d'un formulaire";
// the list values database
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
Vamos rever o significado das quatro constantes definidas acima:
título do documento HTML gerado pelo servlet | |
nome DSN da base de dados ODBC da qual o servlet recupera dados | |
nome de utilizador de um utilizador com acesso de leitura à base de dados acima mencionada | |
a sua palavra-passe |
Se o administrador da base de dados DSNValeurs alterar a palavra-passe do utilizador admDbValeurs, o código-fonte do servlet terá de ser modificado e recompilado. Isto não é muito prático. O ficheiro de configuração web.xml do servlet oferece uma alternativa, permitindo a definição de parâmetros de inicialização do servlet através da tag <init-param>:
permite definir o nome do parâmetro | |
define o valor associado ao parâmetro anterior |
O servlet pode aceder aos seus parâmetros de inicialização utilizando os seguintes métodos:
Um método da classe Servlet, da qual deriva a classe HttpServlet utilizada na programação web. Devolve um objeto ServletConfig que permite aceder aos parâmetros de configuração do servlet. | |
Um método da classe ServletConfig que devolve o valor do parâmetro de inicialização "parameter" |
Configuramos a aplicação "liste" com o seguinte novo ficheiro web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs2</servlet-name>
<servlet-class>gener5</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</init-param>
<init-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</init-param>
<init-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs2</servlet-name>
<url-pattern>/valeurs2</url-pattern>
</servlet-mapping>
</web-app>
Na aplicação de lista, definimos um segundo servlet chamado lstValeurs2 associado ao ficheiro de classe gener5. Este ficheiro foi colocado em <application>\WEB-INF\classes:

O servlet lstValeurs2 tem quatro parâmetros de inicialização: title, DSNValeurs, admDbValeurs e mdpDbValeurs. Além disso, foi definido o alias /values2 para o servlet através da tag <servlet-mapping>. Assim, o servlet lstValeurs2 na aplicação de lista estará acessível através do URL http://localhost:8080/liste/valeurs2.
O código-fonte do servlet foi modificado da seguinte forma para recuperar os parâmetros de inicialização do servlet:
public class gener5 extends HttpServlet{
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
...............
// servlet initialization
public void init(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
DSNValeurs=config.getInitParameter("DSNValeurs");
admDbValeurs=config.getInitParameter("admDbValeurs");
mdpDbValeurs=config.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// table of values is filled from a ODBC database
// name DSN : DSNvaleurs
...............
Para testar o servlet, é necessário reiniciar o Tomcat para que este possa carregar o novo ficheiro de configuração web.xml para a aplicação «liste». Utilizando um navegador, aceda ao URL do servlet: http://localhost:8080/liste/valeurs2:

Se algum dos parâmetros de inicialização exigidos pelo servlet estiver em falta no ficheiro web.xml, é apresentada a seguinte página:

3.3.5. Parâmetros de inicialização para uma aplicação Web
No exemplo anterior, apenas o servlet lstValeurs2 tem acesso aos parâmetros title, DSNValeurs, admDbValeurs e mdpDbValeurs. É possível que outro servlet na mesma aplicação liste necessite de dados da mesma base de dados utilizada pelo servlet lstValeurs2. Nesse caso, os parâmetros DSNValeurs, admDbValeurs e mdpDbValeurs teriam de ser redefinidos na secção de configuração do ficheiro web.xml do novo servlet. Outra solução consiste em definir parâmetros comuns a vários servlets ao nível da aplicação, em vez de ao nível do servlet. O novo ficheiro web.xml para a aplicação fica da seguinte forma:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs3</servlet-name>
<url-pattern>/valeurs3</url-pattern>
</servlet-mapping>
</web-app>
O novo servlet chama-se lstValeurs3, está ligado ao ficheiro de classe gener6 e foi associado ao alias /valeurs3 (mapeamento de servlet). O parâmetro title é o único parâmetro que foi mantido na definição do servlet. Os outros foram colocados na configuração da aplicação dentro de tags <context-param>. Esta tag é utilizada para definir informações específicas da aplicação, e não de um servlet ou página JSP em particular. Como é que o servlet Java acede a estes parâmetros, frequentemente chamados de parâmetros de contexto? Os métodos disponíveis para obter informações de contexto são muito semelhantes aos utilizados para obter parâmetros de inicialização específicos do servlet:
um método da classe Servlet da qual deriva a classe HttpServlet utilizada na programação web. Devolve um objeto ServletContext que fornece acesso aos parâmetros de configuração da aplicação | |
método da classe ServletContext que devolve o valor do parâmetro de inicialização "parameter" |
A classe gener6.java faz apenas as seguintes alterações ao código Java da classe gener5.java utilizada anteriormente:
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// table of values is filled from a ODBC database
// name DSN : DSNvaleurs
...............
O parâmetro title específico do servlet é obtido através de um objeto ServletConfig. Os outros três parâmetros definidos ao nível da aplicação são obtidos através de um objeto ServletContext. Compilamos esta classe e colocamo-la, tal como as outras, em <application>\WEB-INF\classes:

Reiniciamos o Tomcat para que ele tenha em conta o novo ficheiro web.xml da aplicação e solicitamos a URL http://localhost:8080/liste/valeurs3:

3.3.6. Parâmetros de inicialização para uma página JSP
Já vimos como definir parâmetros de inicialização para um servlet ou uma aplicação web. Será que podemos fazer o mesmo para uma página JSP? Voltemos ao início do código da página listvaleurs.jsp que já estudámos:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// variables globales de l'application
// le titre de la page
private final String title="Génération d'un formulaire";
// la base de données des valeurs de liste
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
.........
Encontramos as quatro constantes title, DSNValues, admDbValues e mdpDbValues definidas no ficheiro web.xml da aplicação. As constantes DSNValues, admDbValues e mdpDbValues foram agora definidas ao nível da aplicação, pelo que podemos assumir que uma página JSP pertencente a esta aplicação terá acesso às mesmas. É de facto esse o caso. Sabemos que a página JSP será traduzida num servlet. O servlet terá acesso ao contexto através do método getServletContext(). O caso da constante title é mais delicado. Na verdade, definimo-la ao nível do servlet em vez de ao nível da aplicação, da seguinte forma:
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
Para a página JSP, a sintaxe anterior já não é adequada, uma vez que o conceito de ficheiro de classe já não se aplica. No entanto, a sintaxe de configuração para uma página JSP é muito semelhante à de um servlet. É a seguinte:
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
...
</servlet>
Na verdade, uma página JSP é tratada como um servlet ao qual é atribuído um nome (servlet-name). Em vez de associar um ficheiro de classe a este servlet, associamos o ficheiro fonte da página JSP a ser executada (jsp-file). Assim, as linhas anteriores definem um servlet chamado JSPlstValues associado à página JSP /listvalues2.jsp. O caminho /listvalues2.jsp é relativo à raiz da aplicação. Assim, no caso da nossa aplicação list, o ficheiro listvalues2.jsp estaria localizado na pasta docBase (ver server.xml) da aplicação list:

A configuração da página JSP será a seguinte no ficheiro web.xml da aplicação:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
.......
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
<init-param>
<param-name>JSPtitle</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
..........
<servlet-mapping>
<servlet-name>JSPlstValeurs</servlet-name>
<url-pattern>/jspvaleurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
........
</web-app>
A página JSP listvaleurs2.jsp está localizada no diretório raiz da aplicação liste e está associada ao nome do servlet JSP JSPlstValeurs (servlet-name), que por sua vez está associado ao alias /jspvaleurs (servlet-mapping). Assim, a nossa página JSP estará acessível através do URL http://localhost:8080/liste/jspvaleurs.
A página JSP original listvaleurs.jsp é renomeada para listvaleurs2.jsp e recupera os seus quatro parâmetros de inicialização no método jspInit():
<%!
// application global variables
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// list values
private String[] valeurs=null;
// error msg
private String msgErreur=null;
// initialization of page JSP - executed only once
public void jspInit(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("JSPtitle");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// fills the table of values from a ODBC database
// name DSN : DSNvaleurs
..............
A página JSP recupera os seus parâmetros de inicialização da mesma forma que os servlets. O ficheiro anterior é guardado no diretório raiz da aplicação web «liste»:

O servidor Tomcat é reiniciado para forçá-lo a recarregar o novo ficheiro de configuração web.xml da aplicação. Pode então aceder ao URL http://localhost:8080/liste/jspvaleurs:

3.3.7. Colaboração entre Servlets e JSP numa Aplicação Web
Quando um cliente faz uma solicitação a um servidor web, a resposta pode ser gerada por vários servlets e páginas JSP. Até agora, a resposta era gerada por um único servlet ou página JSP. Vimos que a página JSP oferece melhor legibilidade da estrutura do documento HTML gerado. No entanto, ela geralmente também contém muito código Java. Podemos melhorar isso colocando
- o código Java que não gera o código HTML da resposta em um ou mais servlets
- nas páginas JSP, o código para gerar os vários documentos HTML enviados em resposta ao cliente
Isto deverá ajudar a melhorar a separação entre o código Java e o código HTML. Vamos aplicar esta nova estrutura à nossa aplicação de lista: um servlet Java chamado lstValeurs4 será responsável por ler os valores da base de dados no arranque e, em seguida, processar os pedidos do cliente em . Dependendo do resultado desta análise, o pedido do cliente será direcionado para uma página de erro (erreur.jsp) ou para a página que apresenta a lista de números (liste.jsp). A aplicação de lista consistirá, portanto, num servlet e em duas páginas JSP.
Como é que um servlet pode passar a solicitação que recebeu de um cliente para outro servlet ou para uma página JSP? Iremos utilizar os seguintes métodos:
da classe ServletContext, que devolve um objeto RequestDispatcher. O parâmetro url é o nome da URL para a qual queremos reencaminhar a solicitação do cliente. Este reencaminhamento de solicitação só pode ocorrer dentro da mesma aplicação. Por conseguinte, o parâmetro url é um caminho relativo à estrutura de diretórios web dessa aplicação. | |
Um método da interface RequestDispatcher que encaminha a solicitação do cliente e o objeto de resposta — que deve ser usado para construir a resposta — para a URL anterior. | |
Quando um servlet ou uma página JSP encaminha um pedido para outro servlet ou página JSP, geralmente precisa de transmitir outras informações além do próprio pedido do cliente — informações derivadas do seu próprio processamento do pedido. O método setAttribute da classe ServletRequest permite adicionar atributos ao objeto de pedido do cliente num formato semelhante a um dicionário de pares (atributo, valor), em que atributo é o nome do atributo e valor é qualquer objeto que represente o seu valor. | |
permite-lhe recuperar os valores dos atributos de uma solicitação. Este método será utilizado pelo servlet ou pela página JSP para o qual a solicitação foi encaminhada, a fim de obter as informações adicionadas à mesma. |
O servlet responsável pelo processamento do formulário será configurado da seguinte forma no ficheiro web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
............
<servlet>
<servlet-name>lstValeurs4</servlet-name>
<servlet-class>gener7</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>JSPerreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
<init-param>
<param-name>JSPliste</param-name>
<param-value>/liste.jsp</param-value>
</init-param>
<init-param>
<param-name>URLservlet</param-name>
<param-value>/liste/valeurs4</param-value>
</init-param>
</servlet>
...........
<servlet-mapping>
<servlet-name>lstValeurs4</servlet-name>
<url-pattern>/valeurs4</url-pattern>
</servlet-mapping>
.......
</web-app>
O servlet lstValeurs4 terá quatro parâmetros de inicialização específicos:
o título do documento HTML a ser gerado | |
o URL da página JSP de erro | |
A URL da página JSP que exibe a lista de números | |
A URL associada ao atributo action do formulário apresentado pela página JSPlist. Esta URL será a do servlet lstValeurs4 |
O servlet terá o alias /valeurs4 (servlet-mapping) e, portanto, estará acessível através da URL http://localhost:8080/liste/valeurs4. Está ligado ao ficheiro de classe gener7.java, cujo código-fonte completo é o seguinte:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener7 extends HttpServlet{
// page title
private String title=null;
// the list values database
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// JSP display pages
private String JSPerreur=null;
private String JSPliste=null;
// the URL of the servlet
private String URLservlet=null;
// list values
private String[] valeurs=null;
// error msg
private String msgErreur=null;
// -----------------------------------------------------------------
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// put msgErreur,title in the query attributes
request.setAttribute("msgErreur",msgErreur);
request.setAttribute("title",title);
request.setAttribute("URLservlet",URLservlet);
// was there an error loading the servlet?
if(msgErreur!=null){
// we hand over to a JSP error page
getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
// end
return;
}
// there was no error
// put the list of values in the query attributes
request.setAttribute("valeurs",valeurs);
// we retrieve the user's possible choice
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
request.setAttribute("choix",choix);
// hand over to the JSP list presentation page
getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
// end
return;
}//GET
// -----------------------------------------------------------------
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// returns to GET
doGet(request,response);
}//POST
// -----------------------------------------------------------------
// servlet initialization
public void init(){
// retrieve servlet initialization parameters
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
JSPerreur=config.getInitParameter("JSPerreur");
JSPliste=config.getInitParameter("JSPliste");
URLservlet=config.getInitParameter("URLservlet");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//have all the parameters been recovered?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
msgErreur="Configuration incorrecte";
return;
}
// fills the table of values from a ODBC database
// name DSN : DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// connection to the ODBC database
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// statement object
st=connexion.createStatement();
// execute select query to retrieve values
rs=st.executeQuery("select valeur from Tvaleurs");
// values are retrieved and put into a dynamic table
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// the value is saved in the
lstValeurs.add(rs.getString("valeur"));
}//while
// transformation list --> table
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// problem
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//init
}//class
A nova funcionalidade desta classe é que ela redireciona o pedido do cliente para a página JSPerreur em caso de erro e, caso contrário, para a página JSPliste. A classe não gera a resposta por si própria. As páginas JSP JSPerreur e JSPliste tratam disso. Anteriormente, o servlet adicionava atributos (setAttribute) ao pedido do cliente:
- uma mensagem de erro msgError em caso de erro para a página JSPerreur
- os valores (values) a exibir, o valor selecionado (choice) pelo utilizador, o título do formulário (title) e o URL (URLservlet) do atributo action do formulário para a página JSPliste
Esta classe é compilada e colocada nas classes da aplicação:

A página JSP que exibe uma mensagem de erro é configurada da seguinte forma:
<servlet>
<servlet-name>JSPerreur</servlet-name>
<jsp-file>/erreur.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
</servlet>
.........
<servlet-mapping>
<servlet-name>JSPerreur</servlet-name>
<url-pattern>/JSPerreur</url-pattern>
</servlet-mapping>
O ficheiro JSP associado à página de erro chama-se error.jsp e está localizado na raiz da aplicação:

Tem o alias /JSPerreur, o que o torna acessível através do URL http://localhost:8080/liste/JSPerreur. Possui um parâmetro de inicialização chamado mainServlet, cujo valor é o alias do servlet principal descrito acima. Note-se que este alias é relativo à raiz da aplicação liste; caso contrário, seria /liste/valeurs4. O código da página erreur.jsp é o seguinte:
<%
// code de _jspService
// on récupère le paramètre d'initialisation mainServlet
String servletListValeurs=config.getInitParameter("mainServlet");
// on récupère l'attribut msgErreur
String msgErreur=(String)request.getAttribute("msgErreur");
// attribut valide ?
if(msgErreur!=null){
%>
<!-- code HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %>)</h3>
</body>
</html>
<%
} else { // attribut msgErreur invalide - retour à la servlet principale
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}
%>
Esta página deve normalmente ser chamada pelo servlet anterior, que deve passar-lhe o atributo msgError. No entanto, nada o impede de a chamar diretamente se souber o seu URL. Além disso, se verificar que o atributo msgError está em falta, deve passar o pedido para o servlet principal. Aqui, utilizamos uma tag específica para páginas JSP, cuja sintaxe é:
onde URL é a URL do servlet para o qual a solicitação do cliente é encaminhada. Se o atributo msgError estiver presente, a página de erro é exibida.
A página JSP que exibe a lista de números está configurada da seguinte forma:
<servlet>
<servlet-name>JSPliste</servlet-name>
<jsp-file>/liste.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
.........
<servlet-mapping>
<servlet-name>JSPliste</servlet-name>
<url-pattern>/JSPliste</url-pattern>
</servlet-mapping>
O ficheiro JSP associado à página de erro chama-se liste.jsp e está localizado na raiz da aplicação:

O servlet tem o alias /JSPliste, o que o torna acessível através do URL http://localhost:8080/liste/JSPliste. Possui um parâmetro de inicialização chamado mainServlet, cujo valor é o alias do servlet principal. O código da página liste.jsp é o seguinte:
<%-- page d'affichage de la liste des valeurs --%>
<%
// code de jspService
// on récupère le paramètre d'initialisation
String servletListValeurs=config.getInitParameter("mainServlet");
// on récupère les attributs de la requête venant de la servlet principale
String title=(String) request.getAttribute("title");
String[] valeurs=(String[]) request.getAttribute("valeurs");
String choix=(String) request.getAttribute("choix");
String URLservlet=(String) request.getAttribute("URLservlet");
// attributs valides ?
if(title==null || valeurs==null || choix==null){
// il y a un attribut invalide - on passe la main à la servlet
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}//if
%>
<%-- code HTML --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="<%= URLservlet %>">
<select name="cmbValeurs">
<%
// affichage dynamique des valeurs
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// y-avait-il une valeur choisie ?
if(! choix.equals("")){
// on affiche le choix de l'utilisateur
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Esta página funciona como a página error.jsp. Normalmente, deve ser chamada pelo servlet /list/values4 e receber os atributos title, values e choice. Se algum destes parâmetros estiver em falta, o controlo é passado para o servlet URLservlet (/liste/valeurs4). Se todos os parâmetros estiverem presentes, a lista de números é apresentada juntamente com o número escolhido pelo utilizador, se houver algum.
Se solicitar a URL do servlet principal, obtém o seguinte resultado:

com o seguinte código-fonte (Ver/Fonte):
<html>
<head>
<title>Génération d'un formulaire</title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="/liste/valeurs4">
<select name="cmbValeurs">
<option >0</option>
<option >1</option>
<option >2</option>
<option >3</option>
<option >4</option>
<option >6</option>
<option >5</option>
<option >7</option>
<option >8</option>
<option >9</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Este documento HTML foi gerado pela página JSP liste.jsp. Podemos ver que os atributos title, values e URLservlet foram recuperados com sucesso.
Para concluir sobre a colaboração entre servlets e páginas JSP, observamos que as páginas JSP aqui são muito curtas e não contêm código Java que não contribua diretamente para a criação da resposta HTML. A estrutura dos documentos gerados é, assim, mais visível.
3.4. Ciclo de vida de servlets e páginas JSP
3.4.1. O ciclo de vida
Aqui, focamo-nos no ciclo de vida do servlet. O ciclo de vida das páginas JSP decorre deste. Considere um servlet a ser chamado pela primeira vez. Uma instância da classe é então criada pelo servidor web e carregada na memória. Em seguida, ela atenderá à solicitação. Uma vez feito isso, o servlet não é descarregado da memória. Ele permanece lá para atender a outras solicitações, a fim de otimizar os tempos de resposta do servidor. Ele será descarregado quando tiver passado um período de tempo suficientemente longo sem que tenha atendido a nenhuma nova solicitação. Esse tempo é geralmente configurável no servidor web.
Enquanto estiver na memória, o servlet pode processar várias solicitações simultaneamente. O servidor web cria um thread por solicitação, todos os quais utilizam a mesma instância do servlet:
![]() |
Todas as threads acima referidas partilham as variáveis da instância do servlet. Pode ser necessário sincronizar as threads para evitar a corrupção dos dados do servlet. Voltaremos a este assunto mais tarde.
Quando um servlet é carregado, um método específico do servlet é executado:
Para uma página JSP, é o método
public void jspInit(){
}
que é executado. Aqui está um exemplo de uma página JSP que utiliza o método jspInit:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variable d'instance
int compteur;
// méthode pour incrémenter le compteur
public int getCompteur(){
// on incrémente le compteur
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// on le rend
return compteur;
}
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
A página JSP anterior inicializa um contador com o valor 100 em jspInit. Qualquer pedido subsequente ao servlet incrementa e, em seguida, apresenta o valor deste contador:
Na primeira vez:

Na segunda vez:

Como se pode ver acima, entre as duas solicitações, o servlet não foi descarregado; caso contrário, o contador teria sido 101 durante a segunda solicitação. Quando o servlet é descarregado, o método
é executado, caso exista. Para páginas JSP, é o método
public void jspDestroy(){
}
Nestes métodos, pode, por exemplo, fechar ligações à base de dados que foram abertas nos métodos init correspondentes.
3.4.2. Sincronização de pedidos para um servlet
Voltemos à página JSP anterior que incrementa um contador e o devolve ao cliente web. Suponhamos que existem duas solicitações simultâneas. São então criadas duas threads para as executar; estas threads utilizarão a mesma instância de servlet e, portanto, o mesmo contador. Recordemos o código que incrementa o contador:
public int getCompteur(){
// on incrémente le compteur
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// on le rend
return compteur;
}
O incremento do contador foi intencionalmente escrito de forma desajeitada. Suponha que a execução das duas threads decorra da seguinte forma:
![]() |
- No momento T1, a thread TH1 é executada. Ela lê o contador (=145) de myCounter, depois é interrompida e perde o processador. Por isso, não teve tempo de incrementar myCounter e copiar o novo valor para counter.
- No momento T2, a thread TH2 é executada. Ela lê o contador (=145) de myCounter, depois é interrompida e perde o processador. Note-se que as duas threads têm variáveis myCounter diferentes. Elas partilham apenas variáveis de instância, aquelas que são globais para os métodos.
- No momento T3, a thread TH1 recupera o controlo e termina. Por isso, devolve 146 ao seu cliente.
- No momento T4, a thread TH2 recupera o controlo e termina. Ela também devolve 146 ao seu cliente, quando deveria ter devolvido 147.
Trata-se de um problema de sincronização entre threads. Quando a TH1 pretende incrementar o contador, deve impedir que qualquer outra thread faça o mesmo. Para destacar este problema, reescrevemos a página JSP da seguinte forma:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variable d'instance
int compteur;
// méthode pour incrémenter le compteur
public int getCompteur(){
// on lit le compteur
int myCompteur=compteur;
// on s'arrête 10 secondes
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// on incrémente le compteur
compteur=myCompteur+1;
// on le rend
return compteur;
}
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
Aqui, forçámos o thread a parar 10 segundos após a leitura do contador. Deverá, portanto, libertar a CPU, permitindo que outro thread leia um contador que não foi incrementado. Quando fazemos pedidos com um navegador, não notamos qualquer diferença além da espera de 10 segundos antes de obter o resultado.

Agora, se abrirmos duas janelas do navegador e fizermos duas solicitações com intervalo de tempo muito curto entre elas:


Obtemos o mesmo valor do contador. Podemos destacar melhor o problema com um cliente programado em vez de um manual, como o navegador. Aqui está um cliente Perl que é chamado da seguinte forma:
*program URL N*
onde
URL é a URL do servlet de contagem
N é o número de pedidos a fazer a este servlet
Aqui estão os resultados obtidos para 5 pedidos, que demonstram claramente o problema da má sincronização de threads: todos devolvem o mesmo valor do contador.
DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121
O código do cliente Java é o seguinte.
import java.net.*;
import java.util.regex.*;
import java.io.*;
public class clientCompteurJSP {
public static void main(String[] params){
// data
String syntaxe="Syntaxe : pg URL nbAppels";
// parameter verification
if(params.length!=2){
System.err.println(syntaxe);
System.exit(1);
}//if
// URL
URL urlCompteur=null;
try{
urlCompteur=new URL(params[0]);
String query=urlCompteur.getQuery();
if(query!=null) throw new Exception();
}catch (Exception ex){
System.err.println(syntaxe);
System.err.println("URL ["+params[0]+" incorrecte");
System.exit(2);
}//try-catch
// number of calls
int nbAppels=0;
try{
nbAppels=Integer.parseInt(params[1]);
if(nbAppels<=0) throw new Exception();
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("Nombre d'appels ["+params[1]+" incorrect");
System.exit(3);
}//try-catch
// parameters are correct - connections can be made to the URL
try{
getCompteurs(urlCompteur,nbAppels);
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
System.exit(4);
}//try-catch
}//hand
private static void getCompteurs (URL urlCompteur, int nbAppels)
throws Exception {
// does nbAppels at URL urlCompteur
// displays the counter value returned by the web server each time
// remove from urlCompteur the info needed to connect to the tax server
String path=urlCompteur.getPath();
if(path.equals("")) path="/";
String host=urlCompteur.getHost();
int port=urlCompteur.getPort();
if(port==-1) port=urlCompteur.getDefaultPort();
// calls are made to the URL
Socket[] clients=new Socket[nbAppels];
for(int i=0;i<nbAppels;i++){
// connect to the server
clients[i]=new Socket(host,port);
// create a write stream to the server
PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
// request URL - send HTTP headers
OUT.println("GET " + path + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println("");
}//for
// local data
String réponse=null; // server response
// the model searched for in the HTML server response
Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
// the model for a correct answer
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
// the result of the model comparison
Matcher résultat=null;
for(int i=0;i<nbAppels;i++){
// each client reads the response sent by the server
// create customer input/output flows TCP
BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));
// read the 1st line of the answer
réponse=IN.readLine();
// compare the HTTP line with the model of the correct answer
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// we have a URL problem
throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
}//if
// the response is read through to the end of the headers
while((réponse=IN.readLine())!=null && ! réponse.equals("")){
}//while
// that's it for HTTP headers - move on to HTML code
// to retrieve the counter value
boolean compteurTrouvé=false;
while((réponse=IN.readLine())!=null){
// compare the line with the counter model
if(! compteurTrouvé){
résultat=modèleCompteur.matcher(réponse);
if(résultat.find()){
// meter found
System.out.println("Compteur="+résultat.group(1));
compteurTrouvé=true;
}//if
}//if
}//while
// it's over
clients[i].close();
}//for
}//getCompteurs
}//class
Vamos explicar o código anterior:
- O programa aceita dois parâmetros:
- a URL da página JSP para o contador
- o número de clientes a criar para esta URL
- O programa começa, portanto, por verificar a validade dos parâmetros: que existem efetivamente dois, que o primeiro se assemelha a uma URL sintaticamente e que o segundo é um número inteiro maior que 0. Para verificar se a URL está sintaticamente correta, utilizamos a classe URL e o seu construtor URL(String), que cria um objeto URL a partir de uma string como http://istia.univ-angers.fr. É lançada uma exceção se a string não for uma URL sintaticamente válida. Isto permite-nos verificar a validade do primeiro parâmetro.
- Uma vez verificados os parâmetros, o controlo é passado para o procedimento getCompteurs. Este procedimento irá criar nbAppels clientes, todos os quais se ligarão simultaneamente (ou quase) à URL urlCompteur.
- A porta e a máquina à qual os clientes devem ligar-se são derivadas da URL urlCompteur: [URL].getHost() devolve o nome da máquina e [URL].getPort() devolve a porta.
- Um primeiro ciclo permite que cada cliente:
- ligar-se ao servidor web
- solicitar a URL urlCompteur
Neste ciclo, o cliente não aguarda a resposta do servidor. Isto porque queremos que o servidor receba pedidos quase simultâneos.
- Um segundo ciclo permite que cada cliente receba e processe a resposta enviada pelo servidor. O processamento envolve encontrar a linha na resposta que contém o valor do contador e exibi-lo.
Para resolver o problema destacado anteriormente (o mesmo contador a ser enviado a todos os cinco clientes), precisamos de sincronizar os threads do serviço de contagem num único objeto antes de entrar na secção crítica para ler e atualizar o contador. A nova página JSP é a seguinte:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// variables et méthodes globales de la page JSP
// variables d'instance
int compteur;
Object verrou=new Object();
// méthode pour incrémenter le compteur
public int getCompteur(){
// on syncronise la section critique
synchronized(verrou){
// on lit le compteur
int myCompteur=compteur;
// on s'arrête 10 secondes
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// on incrémente le compteur
compteur=myCompteur+1;
}//synchronized
// on le rend
return compteur;
}//getCompteur
// la méthode exécutée au chargement initial de la page
public void jspInit(){
// init compteur
compteur=100;
}
%>
Quando executado, obtêm-se os seguintes resultados:
dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/compteur/compteur3.jsp 5
Compteur= 104
Compteur= 106
Compteur= 105
Compteur= 107
Compteur= 108
A documentação indica que o servidor web pode, por vezes, criar várias instâncias do mesmo servlet. Neste caso, a sincronização anterior deixa de funcionar porque a variável de bloqueio é local a uma única instância e, por isso, não é conhecida pelas outras instâncias. O mesmo se aplica à variável contador. Para torná-las globais para todas as instâncias, escrevemos:
// variable de classe
static int compteur;
static Object verrou=new Object();
O resto do código permanece inalterado.





