Skip to content

3. Introdução aos servlets Java e às páginas JSP

Neste capítulo, encontrar-se-ão diversos exemplos de servlets e páginas JSP. Eles foram testados com o servidor Tomcat, que opera na porta 8080. Seguindo os links da página inicial, é possível acessar exemplos de servlets e páginas JSP. A maioria dos exemplos abaixo foi retirada dos exemplos do Tomcat. Para testá-los, basta iniciar o Tomcat, acessar o URL http://localhost:8080 com um navegador e seguir o link dos servlets.

3.1. Servlets Java

3.1.1. Enviar um conteúdo HTML para um cliente da Web

Analisamos 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>");
    }
}

Ao executar este servlet, obtém-se a seguinte exibição:

Image

Observe os seguintes pontos:

  • é necessário importar classes específicas para os servlets:

import javax.servlet.*;
import javax.servlet.http.*;

A biblioteca javax.servlet nem sempre é fornecida por padrão com o JDK. Nesse caso, é possível baixá-la diretamente do site da Sun.

  • Um servlet é derivado da classe HttpServlet

public class HelloWorld extends HttpServlet {
  • Uma solicitação GET feita ao servlet é processada pelo método doGet

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • Da mesma forma, uma solicitação POST feita ao servlet é processada pelo método doPost

    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • O objeto HttpServletRequest request é o objeto que nos dá acesso à solicitação feita pelo cliente da Web. A resposta do servlet será enviada por meio do objeto HttpServletResponse response
  • O objeto response nos permite definir os cabeçalhos HTTP que serão enviados ao cliente. Por exemplo, o cabeçalho Content-type: text/html é definido aqui por:
        response.setContentType("text/html");
  • Para enviar a resposta ao cliente, o servlet utiliza um fluxo de saída fornecido pelo objeto response:
        PrintWriter out = response.getWriter();
  • Uma vez obtido esse fluxo de saída, o código HTML é gravado nele e, portanto, 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. Recuperar os parâmetros enviados por um cliente web

O exemplo a seguir mostra como um servlet pode recuperar parâmetros enviados pelo cliente web. Um formulário de entrada:

Image

A resposta enviada pelo servlet:

Image

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 novidades em relação ao exemplo anterior:

  • Os parâmetros enviados pelo navegador são recuperados da seguinte maneira:

        String firstName = request.getParameter("firstname");
        String lastName = request.getParameter("lastname");

O método request.getParameter("nomParamètre") retorna o ponteiro null, caso o parâmetro nomParamètre não faça parte dos parâmetros enviados pelo cliente web.

  • O formulário especifica que o navegador deve enviar os parâmetros por meio do método POST
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
  • Os parâmetros recebidos serão processados pelo método doPost do servlet. Nesse caso, esse método se limita a chamar o método doGet. Assim, esse servlet processa os valores do formulário, independentemente de terem sido enviados por um GET ou por um POST.

    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        doGet(request, response);
    }

3.1.3. Recuperar os cabeçalhos HTTP enviados por um cliente web

O servlet a seguir mostra como recuperar os cabeçalhos HTTP enviados pelo cliente web:

Image

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);
        }
    }
}

Observações:

  • São o objeto request e seu método getHeaderNames que nos dão acesso aos cabeçalhos HTTP enviados pelo navegador na forma de uma enumeração:
        Enumeration e = request.getHeaderNames();
  • O método request.getHeader("cabeçalho") permite obter um cabeçalho HTTP específico. O exemplo acima apresenta alguns deles. É importante lembrar que os cabeçalhos apresentados aqui são enviados pelo navegador. O servidor também possui seus próprios cabeçalhos HTTP, que às vezes reproduzem os do navegador. Os cabeçalhos HTTP enviados pelo navegador têm como objetivo informar ao servidor sobre as capacidades do navegador.
Header
Significado
User-Agent
identidade do navegador
Accept
os formatos MIME aceitos pelo navegador. Assim, image/gif significa que o navegador sabe processar imagens no formato GIF
Host
no formato hote:port. Indica qual máquina e qual porta o navegador deseja acessar.
Accept-Encoding
formato de codificação aceito pelo navegador para os documentos enviados pelo servidor. Assim, se um servidor tiver um documento em formato normal não compactado e outro no formato compactado gzip, e o navegador tiver indicado que sabe processar o formato gzip, o servidor poderá enviar o documento no formato gzip para economizar largura de banda.
Accept-language
idiomas aceitos pelo navegador. Se um servidor tiver o mesmo documento em vários idiomas, ele enviará aquele cujo idioma seja aceito pelo navegador.
Referer
o URL solicitado pelo navegador
Connection
o modo de conexão solicitado pelo navegador. Keep-alive significa que o servidor não deve interromper a conexão após enviar a página solicitada ao navegador. Se este último detectar que a página recebida contém links para imagens, por exemplo, poderá fazer novas solicitações ao servidor para obtê-las sem precisar criar uma nova conexão. Será o navegador que tomará a iniciativa de encerrar a conexão assim que tiver recebido todos os elementos da página.

3.1.4. Recuperar informações do ambiente

O servlet a seguir mostra como acessar informações do ambiente de execução do servlet. Algumas dessas informações são enviadas na forma de cabeçalhos HTTP pelo navegador e, portanto, podem ser recuperadas pelo método anterior.

Image

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);
    }
}

As informações aqui são obtidas por diversos 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());

Segue abaixo uma lista de alguns dos métodos disponíveis e seus significados:

método
significado
getServerName()
o nome do servidor web
getServerPort()
a porta de trabalho do servidor web
getMethod()
o método GET ou POST utilizado pelo navegador para realizar sua solicitação
getRemoteHost()
o nome do computador cliente a partir do qual o navegador fez a solicitação
getRemoteAddr()
o endereço IP dessa mesma máquina
getContentType()
o tipo de conteúdo enviado pelo navegador (cabeçalho HTTP Content-type)
getContentLength()
o número de caracteres enviados pelo navegador (cabeçalho HTTP Content-length)
getProtocol()
a versão do protocolo HTTP solicitada pelo navegador
getRequestURI()
o URI solicitado pelo navegador. Corresponde à parte do URL localizada após a identificação hote:port em http://hote:port/URI

3.1.5. Criar um servlet com JBuilder e implantá-lo com o Tomcat

Descreveremos agora como criar e executar um servlet Java. Utilizaremos duas ferramentas: o JBuilder para compilar o servlet e o Tomcat para executá-lo. O Tomcat por si só poderia ser suficiente. No entanto, ele oferece recursos limitados de depuração. Retomamos o exemplo desenvolvido anteriormente, que exibe os parâmetros recebidos pelo servidor. O servlet envia, em primeiro lugar, o seguinte formulário de entrada:

Image

A resposta enviada pela servlet:

Image

O código-fonte da 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 myRequestParamExample com o JBuilder e inclua nele o programa myRequestParamExample.java anterior.
  • Durante a compilação, pode ocorrer o seguinte problema: seu JBuilder não possui necessariamente a biblioteca javax.servlet necessária para a compilação dos servlets. Nesse caso, é necessário configurar o JBuilder para que ele utilize bibliotecas de classes adicionais. O procedimento está descrito nos anexos deste documento para o JBuilder 7. Repetimos parte dele aqui:
  • ative a opção Tools/Configure JDKs ou (Options/Configurar JDK)

Image

Na seção JDK Settings acima, normalmente há no campo Name a versão JDK 1.3.1. Se você tiver uma versão mais recente do JDK, use o botão Change para indicar o diretório de instalação dessa versão. Acima, foi especificado o diretório E:\Program Files\jdk14, onde estava instalado um JDK 1.4. A partir de agora, o JBuilder utilizará esse JDK para suas compilações e execuções. Na seção (Class, Source, Documentation), temos a lista de todas as bibliotecas de classes que serão exploradas pelo JBuilder; neste caso, as classes do JDK 1.4. As classes deste último não são suficientes para o desenvolvimento web em Java. Para adicionar outras bibliotecas de classes, usa-se o botão Add e indica-se os arquivos .jar adicionais que se deseja utilizar. Os arquivos .jar são bibliotecas de classes. O Tomcat 4.x traz consigo todas as bibliotecas de classes necessárias para o desenvolvimento web. Elas estão localizadas em <tomcat>\common\lib, onde <tomcat> é o diretório de instalação do Tomcat:

Image

Com o botão Add, vamos adicionar essas bibliotecas, uma a uma, à lista de bibliotecas exploradas pelo JBuilder:

Image

A partir de agora, é possível compilar programas Java em conformidade com a norma J2EE, incluindo servlets Java. O JBuilder serve apenas para a compilação; a execução é garantida posteriormente pelo Tomcat.

  • Agora você pode compilar o programa myRequestParamExample.java e gerar o servlet myRequestParamExample.class. Onde colocar esse servlet? Se a configuração inicial do Tomcat não tiver sido alterada, os arquivos .class dos servlets devem ser colocados em <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
  • Verifique se o Tomcat está em execução e, usando um navegador, acesse o URL em http://localhost:8080/examples/servlet/myRequestParamExample:

Image

3.1.6. Exemplos

Para os exemplos a seguir, utilizamos o método descrito anteriormente:

  • compilação do código-fonte XX.java do servlet com o JBuilder
  • implantação do servlet XX.class em <tomcat>\webapps\examples\WEB-INF\classes
  • Após iniciar o Tomcat, acesse com um navegador o endereço URL http://localhost:8080/examples/servlet/XX

3.1.6.1. Geração dinâmica de formulário - 1

Tomaremos como exemplo a geração de um formulário com apenas um controle: uma lista. O conteúdo dessa lista é construído dinamicamente com valores extraídos de uma matriz. Na prática, esses valores costumam ser obtidos de um banco de dados. O formulário é o seguinte:

Image

Se, no exemplo acima, executarmos o comando Envoyer, obtemos a seguinte resposta:

Image

Observe-se que o código URL que gera a resposta é o mesmo que exibe o formulário. Aqui, temos um servlet que processa por conta própria a resposta ao formulário que ele mesmo enviou. Esse é um caso 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>

Observe-se que os valores enviados pelo formulário são transmitidos pelo 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{
    // variáveis de instância
    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{

      // informa-se ao cliente o tipo de documento enviado
      response.setContentType("text/html");
       // o formulário é enviado
      PrintWriter out=response.getWriter();
            // início
      out.println(HTML1);
            // menu suspenso
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            for (int i=0;i<valeurs.length;i++){
                out.println("<option>"+valeurs[i]+"</option>");
            }//para
            out.println("</select>");
            // fim do formulário
            out.println(HTML2+HTML3);
    }//GET

         // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException{

             // recuperamos a escolha do usuário
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) doGet(request,response);

             // prepara-se a resposta
            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>";
             // informa-se ao cliente o tipo de documento enviado
            response.setContentType("text/html");
             // envia-se o formulário
            PrintWriter out=response.getWriter();
            out.println(réponse);
    }//POST
    }//classificação

O método doGet serve para gerar o formulário. Há uma parte dinâmica, que é o conteúdo da lista, proveniente, neste caso, de uma tabela. O método doPost serve para gerar a resposta. Aqui, a única parte dinâmica é o valor da escolha feita pelo usuário na lista do formulário. Esse valor é obtido por meio de request.getParameter("cmbValeurs"), onde cmbValeurs é o nome da lista:

      <select name="cmbValeurs" size="1">

Para concluir, observe os seguintes pontos:

  • o navegador envia os valores do formulário para o servlet que o gerou, pois a tag <form> não possui o atributo <action>. Nesse caso, o navegador envia os dados inseridos no formulário para o URL que o forneceu.
  • A tag <form> especifica que os dados do formulário devem ser enviados pelo método POST. É por isso que esses valores são recuperados pelo método doPost do servlet.

3.1.6.2. Geração dinâmica de formulário - 2

Retomamos o exemplo anterior, modificando-o da seguinte maneira. O formulário proposto continua sendo o mesmo:

Image

A resposta é diferente:

Image

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

Image

e, em seguida, executar Envoyer. Ele obtém a seguinte resposta:

Image

O código do servlet chamado gener2.java é o seguinte:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class gener2 extends HttpServlet{
        // variáveis de instância
        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{

            // recupera-se a eventual escolha do usuário
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // informa-se ao cliente o tipo de documento enviado
            response.setContentType("text/html");
             // envia-se o formulário
            PrintWriter out=response.getWriter();
            // início
            out.println(HTML1);
            // menu suspenso
            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>");
            }//para
            out.println("</select>");
            // continuação do formulário
            out.println(HTML2);
            if(! choix.equals("")){
                // exibe a escolha do usuário
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//se
             // fim do formulário
            out.println(HTML3);
        }//GET

        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // redireciona para GET
            doGet(request,response);
        }//POST
    }//classe

O método doGet realiza todas as tarefas: ele cria o formulário que é enviado ao cliente e processa os valores que este retorna. Os pontos a serem observados são os seguintes:

  • verifica-se se o parâmetro cmbValeurs possui um valor.
  • Se for o caso, ao criar o conteúdo da lista, compara-se cada elemento da lista com a escolha do usuário para atribuir o atributo selected ao elemento escolhido pelo usuário: <option selected>elemento</option>. Além disso, exibe-se abaixo do formulário o valor da escolha.

3.1.6.3. Geração dinâmica de formulário - 3

Retomamos o mesmo problema de antes, mas, desta vez, os valores são obtidos de um banco de dados. No nosso exemplo, trata-se do banco de dados MySQL:

  • o banco de dados se chama dbValeurs
  • seu proprietário é admDbValeurs, cuja senha é mdpDbValeurs
  • O banco de dados possui uma única tabela chamada tvaleurs
  • essa tabela possui apenas um campo inteiro chamado valor
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 por um driver ODBC para MySQL. Seu nome 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{
        // o título da página
        private final String title="Génération d'un formulaire";
         // o banco de dados dos valores da lista
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // valores da lista
        private String[] valeurs=null;
         // mensagem de erro
        private String msgErreur=null;
        // código 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{

            // informa-se ao cliente o tipo de documento enviado
            response.setContentType("text/html");
             // fluxo de saída
            PrintWriter out=response.getWriter();

            // a inicialização do servlet ocorreu normalmente?
            if (msgErreur!=null){
                 // ocorreu um erro — é gerada uma página de erro
                out.println("<html><head><title>"+title+"</title></head>");
                out.println("<body><h3>Application indisponible ("+msgErreur+
                                        ")</h3></body></html>");
                return;
            }//if

             // recuperamos a eventual escolha do usuário
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // envia-se o formulário
             // início
            out.println(HTML1);
            // menu suspenso
            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>");
            // continuação do formulário
            out.println(HTML2);
            if(! choix.equals("")){
                // exibe a escolha do usuário
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//se
             // fim do formulário
            out.println(HTML3);
        }//GET

        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // redireciona para GET
            doGet(request,response);
        }//POST

        // inicialização do servlet
        public void init(){
             // preenche a matriz de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // conexão com o banco de dados ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // objeto Statement
                st=connexion.createStatement();
                 // execução da consulta SELECT para recuperar os valores
                rs=st.executeQuery("select valeur from Tvaleurs");
                // os valores são recuperados e inseridos em uma tabela dinâmica
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // o valor é registrado na lista
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // transformação de lista em tabela
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//inicializar
    }//classe

Os pontos importantes a serem observados são os seguintes:

  1. Um servlet pode ser inicializado por um método cuja assinatura deve ser public void init(). Esse método é executado apenas no carregamento inicial do servlet
  2. Uma vez carregada, uma servlet permanece na memória o tempo todo. Isso significa que, após atender a um cliente, ela não é descarregada. Assim, ela responde mais rapidamente às solicitações dos clientes.
  3. Em nosso servlet, uma lista de valores deve ser buscada em um banco de dados. Como essa lista não se altera ao longo do tempo, o método init é o momento ideal para recuperá-la. Assim, o banco de dados é acessado apenas uma vez pelo servlet, no momento do carregamento inicial deste, e não a cada solicitação de um cliente.
  4. O acesso a um banco de dados pode falhar. O método init do nosso servlet gera uma mensagem de erro msgErreur em caso de falha. Essa mensagem é verificada no método doGet e, caso tenha ocorrido um erro, o método doGet gera uma página informando sobre o erro.
  5. A implementação do método init utiliza um acesso convencional a um banco de dados com os drivers ODBC-JDBC. Se necessário, o leitor é convidado a revisar os métodos de acesso a bancos de dados JDBC.

Ao executar o servlet e caso o servidor MySQL não tenha sido iniciado, é exibida a seguinte página de erro:

Image

Se, agora, iniciarmos o servidor MySQL, obtemos a página:

Image

Se selecionarmos o número 6 e clicarmos em “enviar”:

Image

3.1.6.4. Recuperar os valores de um formulário

Vamos retomar um exemplo já visto, o do seguinte formulário da web:

Image

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");
      }//apagar
        </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> do formulário foi definida da seguinte forma:

    <form method="POST" action="http://localhost:8080/examples/servlet/parameters">

O navegador “enviará” os valores do formulário para o URL http://localhost:8080/examples/servlet/parameters, que é o URL de um servlet gerenciado pelo Tomcat e que exibe os valores do formulário anterior. Se chamarmos a servlet parameters diretamente, obtemos os seguintes resultados:

Image

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

Image

e clicarmos no botão Enviar (do tipo submit), a servlet parameters é chamada, desta vez, com parâmetros. Ela retorna, então, a seguinte resposta:

Image

Nessa resposta, podemos ver claramente os valores inseridos 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{
    // variáveis de instância
    String title="Récupération des paramètres d'un formulaire";

    private String getParameter(HttpServletRequest request, String contrôle){
      // retorna o valor request.getParameter (verificação) ou "" caso não exista
      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
    {
      // começa-se por recuperar os parâmetros do formulário
      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");

      // indica-se o conteúdo do documento
        response.setContentType("text/html");
       // envia-se o documento
        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
    {
       // redireciona para GET
      doGet(request,response);
    }
}

Neste código, encontramos as técnicas apresentadas anteriormente em outro exemplo. Vale destacar dois pontos:

  1. o controle lst2 é uma lista de seleção múltipla e, portanto, vários elementos podem ser selecionados. É o caso do nosso exemplo, em que os elementos liste1 e liste3 foram selecionados. Os valores de lst2 foram transmitidos pelo navegador ao servidor na forma lst2=liste1&lst2=liste3. O servlet Java pode recuperar esses valores em um array com o método getParameterValues: aqui, request.getParameterValues("lst2") fornece um array com duas cadeias de caracteres ["liste1","liste3"].
  2. O controle areaSaisie é um campo de entrada multilinha. request.getParameter("areaSaisie") retorna o conteúdo do campo na forma de uma única sequência de caracteres. Se, nessa sequência, quisermos recuperar as diferentes linhas que a compõem, poderemos utilizar a função split da classe String. O código a seguir
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");

recupera as linhas do campo de entrada. Essas linhas são terminadas pelos caracteres \r\n (0D0A).

Para realizar os testes, nós:

  • construímos e compilamos o servlet parameters com 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.
  • acessei o URL http://localhost:81/html/balises2.htm, cujo código foi apresentado acima
  • preenchi o formulário e cliquei no botão Envoyer.

3.1.6.5. Recuperar os cabeçalhos HTTP de um cliente web

Retomamos o mesmo exemplo anterior, mas, em resposta ao cliente web que enviou os valores do formulário, enviamos a ele os cabeçalhos HTTP que ele enviou ao mesmo tempo. Introduzimos uma única alteração em nosso formulário:

    <form method="GET" action="http://localhost:8080/examples/servlet/headers">

Os valores do formulário serão enviados pelo método GET para um servlet Java chamado headers, localizado em <tomcat>\webapps\examples\WEB-INF\classes. O servlet headers foi criado e compilado junto 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
    {
       // define-se a natureza do documento
        response.setContentType("text/html");
       // obtém-se um fluxo de gravação
        PrintWriter out = response.getWriter();
      // exibição da lista de cabeçalhos 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
}

Solicitamos o URL http://localhost:81/html/balises2.htm e geramos o Envoyer sem alterar o formulário. Obtemos a seguinte resposta:

Image

Observe-se que o parâmetro URL está presente no campo Address do navegador, o que mostra a forma (GET) utilizada para transmitir os parâmetros. Retomamos o mesmo exemplo, mas alterando a forma de enviar os parâmetros (POST):

    <form method="POST" action="http://localhost:8080/examples/servlet/headers">

Obtemos a seguinte resposta:

Image

Observe-se que os cabeçalhos HTTP, content-type e content-length são característicos de um envio feito por POST. Além disso, observe-se que, no campo Address do navegador, os valores do formulário não aparecem mais.

3.2. Páginas JSP

As páginas JSP (Java Server Pages) são outra forma de desenvolver aplicativos de servidor web. Na verdade, essas páginas JSP são convertidas em servlets antes de serem executadas, e assim voltamos à tecnologia dos servlets. As páginas JSP permitem destacar melhor a estrutura das páginas HTML geradas. Apresentamos a seguir alguns exemplos, alguns dos quais podem ser acessados seguindo o link JSP na página inicial do Tomcat:

3.2.1. Recuperar informações de ambiente

Retomamos aqui um exemplo já abordado com um servlet: exibir as variáveis de ambiente de um servlet. Trata-se do exemplo snoop dos exemplos JSP:

Image

O código-fonte da página JSP está localizado em <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) ou em <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>

Observamos os seguintes pontos:

  • temos aqui um código que se assemelha bastante ao HTML. No entanto, nele encontram-se as tags <%= expressão %>, que são específicas da linguagem JSP. O compilador JSP substitui, no texto HTML, a tag inteira pelo valor de expression.
  • Este exemplo utiliza os métodos do objeto Java request, que é o objeto request já abordado no estudo sobre servlets. Trata-se, portanto, de um objeto HttpServletRequest. Assim, a tag <%= request.getRemoteHost() %> será substituída no código HTML pelo nome do computador do cliente da web que fez a solicitação.
  • É possível chegar ao mesmo resultado com um servlet, mas, neste caso, a estrutura da página da web fica mais clara.

3.2.2. Recuperar os parâmetros enviados pelo cliente web

Retomamos aqui o exemplo já estudado com um servlet. É apresentado um formulário ao navegador:

Image

Em resposta à solicitação acima, o navegador recebe a seguinte página:

Image

O código da página JSP é o seguinte:


<%
   // variáveis locais da procedimento principal
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- código 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 encontremos a tag <%= expressão %>, já vista no exemplo anterior, surge uma nova tag: <% instruções Java; %>. A tag <% introduz código Java. Esse código termina ao encontrar a tag de fechamento de código %>.
  • Todo o código anterior (HTML + JSP) será convertido em um servlet Java. Ele será encapsulado em um único método, chamado de método principal da página JSP. É por isso que as variáveis Java declaradas no início da página JSP são reconhecidas nas outras partes do código JSP que se encontram espalhadas pelo código HTML: essas variáveis e partes do código farão parte do mesmo método Java. Mas se nosso código JSP contivesse métodos, as variáveis title, firstname e lastname não seriam reconhecidas nele devido ao isolamento entre métodos. Seria necessário transformá-las em variáveis globais ou passá-las como parâmetros para os métodos. Voltaremos a esse assunto.
  • Para incluir partes dinâmicas no código HTML, há 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 servlets, mas não é do mesmo tipo: trata-se de um objeto JspWriter e não de um PrintWriter. Ele permite gravar no fluxo HTML usando 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. As tags JSP

Segue uma lista de tags que podem ser encontradas em uma página JSP e seus significados.

tag
significado
<!-- comentário -->
Comentário HTML. É enviado ao cliente.
<%-- comentário --%>
comentário JSP. Não é enviado ao cliente.
<%! déclarations, méthodes %>
declara variáveis globais e métodos. As variáveis serão reconhecidas em todos os métodos
<%= expression %>
O valor da expressão será inserido na página HTML no lugar da tag
<% code Java %>
contém código Java que fará parte do método principal da página JSP
<%@ page attribut1=valeur1
attribut2=valeur2 … %>
define atributos para a página JSP. Por exemplo:
import="java.util.*,java.sql.*" para especificar as bibliotecas necessárias para a página JSP
extends="umaClassePai" para fazer com que a página JSP seja derivada de outra classe

3.2.4. Os objetos implícitos JSP

Nos exemplos anteriores, encontramos dois objetos não declarados: request e out. Esses são dois dos objetos que são definidos automaticamente no servlet no qual a página JSP é convertida. Eles são chamados de objetos implícitos ou predefinidos. Existem outros, mas esses são os mais utilizados junto com o objeto response:

objeto
significado
HttpServletRequest request
o objeto a partir do qual se tem acesso à solicitação do cliente da Web (getParameter, getParameterNames, getParameterValues)
HttpServletResponse response
o objeto com o qual é possível construir a resposta do servidor Web ao seu cliente. Permite definir os cabeçalhos HTTP a serem enviados ao cliente Web.
JspWriter out
o fluxo de saída que nos permite enviar o código HTML ao cliente (print, println)

3.2.5. A transformação de uma página JSP em um servlet

Vamos retomar o código JSP de myRequestParamExample.jsp:


<%
   // variáveis locais do procedimento principal
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- código 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 essa página JSP ao servidor Tomcat, este a transforma em um servlet. Se a página URL solicitada 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:

Image

Nesse nome, encontramos o http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp da página JSP. Vemos acima que temos acesso ao código Java do servlet gerado para a página JSP. No nosso exemplo, ele é 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();


                   // variáveis locais do procedimento principal
                  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 nos concentrar apenas nos seguintes pontos:

  • O método principal do servlet é o seguinte:
    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

É esse método que é executado no início da servlet. Vemos que ele recebe dois parâmetros: a solicitação request do cliente e um objeto response para gerar sua resposta ao cliente web.

  • No método principal, um objeto JspWriter chamado `out` é declarado e, em seguida, inicializado. É ele que permitirá enviar o código HTML ao cliente por meio das instruções out.print("codeHTML").
        JspWriter out = null;
...
            out = pageContext.getOut();
  • O código Java

<%
   // variáveis locais do procedimento principal
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

foi incorporado na íntegra ao método principal _jspService do servlet. O mesmo se aplica a todo o código localizado entre as tags <%… %>

  • O código HTML da página JSP é alvo das 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 há outros métodos além do método principal _jspService.

3.2.6. Os métodos e variáveis globais de uma página JSP

Consideremos a seguinte página JSP:


<%!
   // a tag anterior inicia a seção de variáveis e métodos globais
   // essa seção será reproduzida sem alterações no servlet
  
   // uma variável global
  String prenom="inconnu";

   // um método  
  private String sonChien(){
    return "milou";
  }//sonChien

   // outro método
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   // fim da parte global do servlet
%>  

<%
   // a tag anterior indica que o código a seguir será gravado
   // no método principal do servlet
  
   // variável local do método principal
  String nom="tintin";
%>


<%-- código 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>
      <%
         // o nome do amigo dele
        afficheAmi(out);
      %>
    </center>
  </body>
</html>

Esta página JSP gera a seguinte página da Web:

Image

Vamos analisar 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>
      <%
         // o nome do amigo dele
        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 elas têm acesso às variáveis nom, prenom e aos métodos sonChien e afficheAmi?

nom (tintin)
é uma variável local do método principal da página JSP e, portanto, reconhecida nesse método
prenom (inconnu)
é uma variável global da página JSP e, portanto, conhecida no método principal
sonChien (milou)
é um método público da página JSP e, portanto, acessível a partir do método principal
afficheAmi (Haddock)
é um método público da página JSP e, portanto, acessível a partir do método principal. Observe-se que o objeto out é passado como parâmetro para o método. Isso é obrigatório neste caso. De fato, 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, após a remoção do 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 {

          // a tag anterior inicia a seção de variáveis e métodos globais
           // essa seção será reproduzida sem alterações no servlet

           // uma variável global
          String prenom="inconnu";

           // um método  
          private String sonChien(){
            return "milou";
          }//sonChien

           // outro método
          private void afficheAmi(JspWriter out) throws Exception{
            out.println("<p>Son ami s'appelle Haddock</p>");
          }//afficheAmi

           // fim da parte global do 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");
                  // a tag anterior indica que o código a seguir será gravado
                   // no método principal do servlet

                   // variável local do método principal
                  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      ");

                        // o nome do amigo dele
                        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);
        }
    }
}

Vemos acima que o código Java que estava entre as tags JSP <%! .. %> foi reproduzido na íntegra e não faz parte do método principal _jspService do servlet. As variáveis declaradas nessa parte são, portanto, variáveis de instância, ou seja, globais para os métodos, e é também ali que se pode definir outros métodos além de _jspService.


  // essa parte será reproduzida sem alterações no servlet

   // uma variável global
  String prenom="inconnu";

   // um método
  private String sonChien(){
    return "milou";
  }//sonChien

   // outro método
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   // fim da parte global do servlet

3.2.7. Implantação e depuração das páginas JSP no servidor Tomcat

Quando se deseja criar uma página JSP e utilizá-la com o servidor Tomcat, surge a questão de onde posicionar a página na estrutura de diretórios do servidor. Existem diferentes maneiras de fazer isso, sobre as quais falaremos mais adiante. Por enquanto, a mais simples é colocar a página JSP em uma pasta 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 do exemplo anterior era http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Isso significa que a página tintin.jsp estava na pasta <tomcat>\webapps\examples\jsp\perso\tintin.

Uma página JSP é convertida em um arquivo-fonte Java, que é então compilado pelo Tomcat quando a página JSP (URL) é solicitada por um navegador. Podem ocorrer erros de compilação. O Tomcat 4.x os sinaliza em sua resposta ao navegador. Ele indica, em particular, as linhas do arquivo .java que estão incorretas. Os erros podem ter diversas causas:

  1. o código JSP da página está incorreto (erros nas tags JSP utilizadas, por exemplo)
  2. o código Java incluído na página JSP está incorreto

A primeira causa pode ser eliminada verificando-se o código JSP da página. A segunda pode ser eliminada verificando-se o código Java. Isso pode ser feito compilando diretamente o arquivo .java gerado para a página JSP com uma ferramenta como o JBuilder, que oferece recursos de depuração mais avançados do que os do Tomcat.

3.2.8. Exemplos

Retomamos o exemplo já abordado com um servlet em que um usuário escolhe um número em uma lista e o servidor informa qual número ele escolheu, ao mesmo tempo em que retorna a mesma lista com o elemento selecionado pelo usuário:

Image

Para construir essa página, recuperamos o código do servlet e o modificamos da seguinte maneira:

  • mantivemos inalterado o código Java que não gerava o código HTML
  • o código Java que gerava o código HTML foi transformado em uma combinação do código HTML com o código JSP

Assim, obtém-se a seguinte página JSP:



<%@ page import="java.sql.*, java.util.*" %>

<%!

         // variáveis globais do aplicativo
         // o título da página
        private final String title="Génération d'un formulaire";
         // banco de dados dos valores da lista
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // valores da lista
        private String[] valeurs=null;
         // mensagem de erro
        private String msgErreur=null;
    
         // inicialização da página JSP — executada apenas uma vez
        public void jspInit(){
             // preenche a tabela de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // conexão com o banco de dados ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // objeto Statement
                st=connexion.createStatement();
                 // execução da consulta SELECT para recuperar os valores
                rs=st.executeQuery("select valeur from Tvaleurs");
                // os valores são recuperados e inseridos em uma tabela dinâmica
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // o valor é registrado na lista
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // transformação de lista em tabela
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//inicialização
   
%>

<%
     // código de _jspService executado a cada solicitação do cliente
   // ocorreu algum erro durante a inicialização da página JSP?
  if(msgErreur!=null){
%>
       <!-- código HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %></h3>
      </body>
    </html>
<%
       // fim de jspService
    return;
  }//se
  
     // recupera-se a eventual escolha do usuário
    String choix=request.getParameter("cmbValeurs");
    if(choix==null) choix="";
%>
  
   <%-- sem erro — código HTML da página normal --%>
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST">
            <select name="cmbValeurs">
              <%
                 // exibição dinâmica dos valores
                          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>");
                          }//para
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
<%
         // havia algum valor selecionado?
                if(! choix.equals("")){
        // exibe a escolha do usuário
%>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%        
                }//if
%>        
      </body>
    </html>

Observe os seguintes pontos:

  • as instruções import do servlet foram alvo de uma diretiva <% page import="..." %>
  • a tag <%! ... %> engloba as variáveis globais e os métodos Java do aplicativo
  • o método init do servlet, que é executado uma única vez, no momento do carregamento do servlet, é chamado para uma página JSP: jspInit. Esses dois métodos têm a mesma função. Portanto, reproduzimos aqui integralmente o código do método init do servlet.
  • As variáveis de instância do servlet, aquelas que devem ser conhecidas em vários métodos, foram reproduzidas exatamente da mesma forma. Trata-se essencialmente das variáveis title, valeurs e msgErreur, 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 no momento de uma solicitação de um cliente.
  • Assim como no servlet, o método _jspService começará verificando o valor da variável msgErreur para determinar se deve gerar uma página de erro. Se houver erro, ela gera a página de erro e encerra a execução (return).
  • Se não houver erro, ela gera o formulário com a lista de valores
  • feito isso, ela verifica se o usuário escolheu um número; nesse caso, exibe esse número na página gerada

O que ganhamos em relação ao servlet? Sem dúvida, uma melhor visualização do código HTML gerado. Mas ainda há muito código Java que “polui” essa visualização. Veremos posteriormente outro método chamado delegação, no qual poderemos colocar a maior parte do código Java em um servlet, ficando a página JSP com apenas os códigos HTML e JSP. Assim, separamos claramente a parte de processamento da parte de apresentação.

3.3. Implantação de uma aplicação web no servidor Tomcat

Apresentamos agora como implantar aplicações web Java no servidor Tomcat. Embora o que será explicado seja específico para esse servidor, a implantação de uma aplicação web Java em outro contêiner J2EE apresentará características semelhantes às que serão descritas a seguir.

3.3.1. Os arquivos de configuração server.xml e web.xml

Até agora, para testar nossos servlets e páginas JSP, colocamos

  • os servlets na pasta <tomcat>\webapps\examples\WEB-INF\classes. Eles ficavam, então, acessíveis pelo endereço http://localhost:8080/examples/servlet/nomServlet
  • as páginas JSP na árvore de diretórios <tomcat>\webapps\examples\jsp. Elas estavam, então, acessíveis por meio do URL http://localhost:8080/examples/jsp/nomPageJSP

Nunca explicamos por que era assim. A configuração do servidor Tomcat é feita em um arquivo de texto chamado server.xml, localizado na pasta <tomcat>\conf:

Image

Esse arquivo de texto é, na verdade, um arquivo XML (eXtended Markup Language). Um documento XML é um documento de texto que contém tags, assim como um documento HTML. No entanto, enquanto as tags da linguagem HTML estão bem definidas, as da linguagem XML não estão. Assim, o documento a seguir é um documento XML:

<personne>
    <prenom>Pierre</prenom>
  <nom>Lucas</nom>
  <age>28</age>
</personne>

Um documento XML é simplesmente um documento “marcado” que segue determinadas regras de marcação:

  • um texto marcado na forma <xx att1="val1" att2="val2" ....>texto</xx>
  • uma tag pode estar sozinha e ter o formato <xx att1="val1" att2="val2" ..../>

Os campos “atti” são chamados de atributos da tag xx e os campos “vali” 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. Ela deveria ser escrita como <br/> para ser válida, a fim de respeitar a regra que determina que toda tag XML deve ser fechada. Uma variante de HTML chamada XHTML foi criada para transformar qualquer documento XHTML em um documento XML válido. Alguns navegadores mais recentes são capazes de exibir arquivos XML. Assim, se chamarmos de personne.xml o documento XML apresentado no exemplo acima e o visualizarmos com o IE6, obteremos a seguinte exibição:

Image

O IE6 reconhece as tags e as destaca com cores. Ele também reconhece a estrutura do documento por meio das tags. Assim, se chamarmos de personne2.xml o seguinte documento:

<personne><prenom>Pierre</prenom><nom>Lucas</nom><age>28</age></personne>

e o visualizarmos com o IE6, obtemos a mesma exibição:

Image

O IE6 reconheceu corretamente a estrutura e o conteúdo do documento. Todo o valor do documento XML reside nessa propriedade: é fácil recuperar a estrutura e o conteúdo de um documento XML. Isso é feito com um programa chamado analisador XML. Os documentos XML tendem a se tornar o padrão nas trocas de documentos na web. Consideremos uma máquina A que precisa enviar um documento DOC para uma máquina B. O documento DOC é gerado a partir das informações contidas em um banco de dados DB-A. Já a máquina B deve armazenar o documento DOC em um banco de dados DB-B. A troca poderá ocorrer da seguinte maneira:

  • a máquina A recupera os dados do banco de dados DB-A e os encapsula em um documento de texto XML
  • o documento XML é enviado à máquina B pela rede
  • a máquina B analisa o documento recebido com um analisador sintático XML e recupera tanto a estrutura quanto os dados (como fez o IE6 em nosso exemplo). Ela pode então armazenar os dados recebidos no banco de dados DB-B

Não falaremos mais sobre a linguagem XML, que mereceria um livro inteiro só para ela.

Aqui, portanto, o Tomcat é configurado pelo arquivo XML server.xml. Se visualizarmos esse arquivo com o IE6, obtemos um documento complexo. Vamos nos concentrar apenas nas seguintes linhas:

Image

É a tag <Context ...> que nos interessa aqui. Ela serve para definir aplicativos web. Dois de seus atributos merecem destaque:

  • path: é o nome da aplicação web
  • docBase: é a pasta na qual ela está localizada. Aqui, trata-se de um nome relativo: examples. Relativo a qual pasta? A resposta também se encontra no arquivo server.xml, na linha seguinte:

Image

A linha acima define o servidor web:

  • name: nome do servidor web
  • appBase: raiz da árvore de documentos que ele distribui. Mais uma vez, temos um nome relativo: webapps. Ele é relativo ao diretório de instalação do servidor Tomcat <tomcat>. Portanto, trata-se da pasta <tomcat>\webapps.

A aplicação web examples tem seus documentos na pasta examples (ver docBase acima). Esse nome é relativo à raiz da árvore de diretórios da web do servidor, c.a.d. <tomcat>\webapps. Trata-se, portanto, da pasta <tomcat>\webapps\examples. Vamos examinar essa pasta mais de perto:

Image

Encontramos aqui a pasta WEB-INF\classes, na qual armazenamos nossos servlets para testá-los. A pasta WEB-INF contém um arquivo chamado web.xml:

Image

Esse arquivo serve para configurar a aplicação web examples. Não entraremos em detalhes sobre esse arquivo, que é muito complexo, por enquanto. Vamos nos concentrar apenas nas seguintes linhas:

    <servlet>
      <servlet-name>
          servletToJsp
      </servlet-name>
      <servlet-class>
          servletToJsp
      </servlet-class>
    </servlet>

A tag <servlet> serve para definir um servlet dentro de uma aplicação web. Vale lembrar que a aplicação web em questão é examples. A tag servlet contém aqui 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 a servlet for solicitada. Neste exemplo, a servlet e sua classe têm o mesmo nome. Isso não é obrigatório.

Como a servlet servletToJsp é solicitada por um navegador ao servidor Tomcat?

  • O navegador solicita o URL http://localhost:8080/examples/servlet/servletToJsp
  • O Tomcat analisa o caminho da servlet /examples/servlet/servletToJsp. Ele interpreta a primeira parte do caminho, /examples, como o nome de uma aplicação web e procura em seu arquivo de configuração, server.xml, onde os documentos dessa aplicação foram armazenados. Como vimos anteriormente, é na pasta <tomcat>\webapps\examples.
  • O Tomcat usa o restante do caminho do servlet para localizá-lo na aplicação web examples. Esse caminho /servlet/servletToJsp indica que ele deve executar o servlet com o nome servletToJsp. O Tomcat então lerá o arquivo de configuração web.xml da aplicação examples, que encontrará em <tomcat>\webapps\examples\WEB-INF. Nesse arquivo, ele verificará que o servlet servletToJsp está vinculado à classe Java servletToJsp (consulte o arquivo web.xml acima). Em seguida, ele procurará essa classe na pasta WEB-INF\classes da aplicação web examples, c.a.d. em <tomcat>\webapps\examples\WEB-INF\classes e a executará.

Image

3.3.2. Exemplo: implantação da aplicação web “lista”

Retomamos um servlet já estudado, que apresentava ao usuário uma lista de números, dentre os quais ele escolhia um. O servlet, em seguida, confirmava o número que ele havia escolhido:

Image

Conforme mostra o campo Address do navegador acima, o arquivo de classe do servlet se chamava gener3. De acordo com as explicações fornecidas anteriormente:

  • o URL /examples/servlet/gener3 mostra que se trata de um servlet chamado gener3 do aplicativo web examples
  • no arquivo web.xml da aplicação examples, não se encontrará nada que mencione uma servlet gener3. Como o Tomcat a encontrou, então? Depois de examinar todo o arquivo web.xml, não posso responder com certeza... A questão permanece em aberto...

Decidimos implantar a servlet gener3.class com o nome lstValeurs em uma aplicação web chamada “liste”, localizada na pasta E:\data\serge\Servlets\lstValeurs:

Image

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

Image

Configuramos a aplicação web “liste” adicionando, no arquivo server.xml, as seguintes linhas acima daquelas que definem a aplicação web manager:

                 <!-- Pessoal: lstValeurs -->
                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

                <!-- Contexto do Tomcat Manager -->
                <Context path="/manager" docBase="manager" debug="0" privileged="true" />
                <!-- Contexto de exemplos do Tomcat -->
                <Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........

A linha que define a lista de aplicativos indica que ela está localizada na pasta e:/data/serge/servlets/lstValeurs. Agora, precisamos definir o arquivo web.xml desse aplicativo. Esse arquivo definirá o único servlet do aplicativo:

<?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 arquivo acima indica que o servlet denominado lstValeurs está associado ao arquivo de classe gener3.class. Esse arquivo web.xml deve ser criado e salvo na pasta WEB-INF da aplicação lista:

Image

A captura de tela acima mostra uma pasta src na qual foi colocado o arquivo de origem gener3.java. Essa pasta pode não existir. Ela não tem utilidade alguma nesta demonstração. Estamos prontos para realizar os testes:

  • desligue e reinicie o Tomcat para que ele recarregue seu arquivo de configuração server.xml. Aqui estamos no Windows. No Unix, é possível forçar o Tomcat a recarregar seu arquivo de configuração sem precisar desligá-lo.
  • Com um navegador, acesse o URL em http://localhost:8080/liste/servlet/lstValeurs

Image

Percebe-se que o URL anterior contém a palavra-chave servlet, assim como todos os URL de servlets utilizados até agora. É possível dispensá-lo associando, no arquivo web.xml da aplicação “lista”, o servlet lstValeurs a um modelo de URL (url-pattern):

<?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 /valores ao servlet lstValeurs definido nas linhas anteriores. Salvamos o novo arquivo web.xml e solicitamos o URL http://localhost:8080/liste/valeurs:

Image

3.3.3. Implantação das páginas públicas de um aplicativo web

Acabamos de ver a implantação de um aplicativo web composto por um único servlet. Um aplicativo web pode ter diversos componentes: servlets, páginas JSP, arquivos HTML, applets Java, etc. Onde esses elementos do aplicativo são colocados? Se <application> é a pasta da aplicação web definida pelo atributo docBase da aplicação no arquivo de configuração do Tomcat server.xml, vimos que os servlets eram colocados em <application>\WEB-INF\classes. Os demais elementos da aplicação podem ser colocados em qualquer lugar na árvore de pastas de <application>, exceto na pasta WEB-INF. Consideremos a aplicação JSP listvaleurs.jsp já estudada:

Image

Essa página JSP havia sido armazenada na pasta <tomcat>\webapps\examples\jsp\perso\listvaleurs. Essa página poderia ser um componente da aplicação “liste” implantada anteriormente. Vamos colocar o arquivo listvaleurs.jsp diretamente na pasta dessa aplicação:

Image

Vamos relembrar a configuração da aplicação liste no arquivo server.xml:

                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

Qualquer URL que comece com o caminho /liste é considerado parte do aplicativo liste e será procurado na pasta indicada. Vamos acessar o URL http://localhost:8080/liste/listvaleurs.jsp com um navegador:

Image

Conseguimos, de fato, a página JSP esperada.

3.3.4. Parâmetros de inicialização de um servlet

Vimos que um servlet é configurado pelo arquivo <application>\WEB-INF\web.xml, onde <application> é a pasta do aplicativo web ao qual ele pertence. É possível incluir nesse arquivo parâmetros de inicialização do servlet. Voltemos ao nosso servlet lstValeurs da aplicação web liste, cujo arquivo 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. No código-fonte dessa classe, encontramos a definição de algumas constantes:

public class gener3 extends HttpServlet{
        // o título da página
        private final String title="Génération d'un formulaire";
         // banco de dados dos valores da lista
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";

Vale lembrar o significado das quatro constantes definidas acima:

title
título do documento HTML gerado pelo servlet
DSNValeurs
nome DSN do banco de dados ODBC de onde o servlet irá buscar os dados
admDbValeurs
nome de um usuário com permissão de leitura na base de dados anterior
mdpDbvaleurs
sua senha

Se o administrador do banco de dados DSNValeurs alterar a senha do usuário admDbValeurs, o código-fonte do servlet deverá ser modificado e recompilado. Isso não é muito prático. O arquivo de configuração da servlet web.xml oferece uma alternativa, permitindo a definição de parâmetros de inicialização da servlet com a tag <init-param>:

    <init-param>
        <param-name>...</param-name>
        <param-value>...</param-value>
    </init-param>
<param-name>
permite definir o nome do parâmetro
<param-value>
define o valor associado ao parâmetro anterior

O servlet tem acesso aos seus parâmetros de inicialização por meio dos seguintes métodos:

[Servlet].getServletConfig()
método da classe Servlet, da qual deriva a classe HttpServlet, utilizada para programação web. Retorna um objeto ServletConfig que dá acesso aos parâmetros de configuração do servlet.
[ServletConfig].getInitParameter("paramètre")
método da classe ServletConfig que retorna o valor do parâmetro de inicialização “paramètre”

Configuramos o aplicativo liste com o novo arquivo web.xml a seguir:

<?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 liste, definimos um segundo servlet chamado lstValeurs2 vinculado ao arquivo de classe gener5. Este último foi colocado em <application>\WEB-INF\classes:

Image

O servlet lstValeurs2 possui quatro parâmetros de inicialização: title, DSNValeurs, admDbValeurs, mdpDbValeurs. Além disso, o alias /valeurs2 foi definido para o servlet por meio da tag <servlet-mapping>. Assim, o servlet lstValeurs2 do aplicativo liste estará acessível por meio do URL e do 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{
    // o título da página
    private String title=null;
    // banco de dados dos valores da lista
    private String DSNValeurs=null;
    private String admDbValeurs=null;
    private String mdpDbValeurs=null;
...............

         // inicialização do servlet
        public void init(){
             // recuperam-se os parâmetros de inicialização do servlet
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            DSNValeurs=config.getInitParameter("DSNValeurs");
            admDbValeurs=config.getInitParameter("admDbValeurs");
            mdpDbValeurs=config.getInitParameter("mdpDbValeurs");

             //todos os parâmetros foram recuperados?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // preenchimento da tabela de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
...............

Para testar o servlet, é necessário reiniciar o Tomcat para que ele reconheça o novo arquivo de configuração web.xml do aplicativo liste. Usando um navegador, acesse o URL da servlet http://localhost:8080/liste/valeurs2:

Image

Se algum dos parâmetros de inicialização necessários para o servlet estiver ausente do arquivo web.xml, será exibida a seguinte página:

Image

3.3.5. Parâmetros de inicialização de uma aplicação web

No exemplo anterior, apenas o servlet lstValeurs2 tem acesso aos parâmetros title, DSNValeurs, admDbValeurs e mdpDbValeurs. Seria possível imaginar que outra servlet da mesma aplicação, liste, precisasse dos dados do mesmo banco de dados utilizado pela servlet lstValeurs2. Nesse caso, seria necessário redefinir os parâmetros DSNValeurs, admDbValeurs e mdpDbValeurs na seção de configuração do arquivo web.xml da nova servlet. Outra solução é definir os parâmetros comuns a várias servlets no nível da aplicação, e não mais no nível das servlets. O novo arquivo web.xml da 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 se chama lstValeurs3, está vinculado ao arquivo de classe gener6 e foi associado ao alias /valeurs3 (servlet-mapping). O parâmetro title é o único parâmetro que foi mantido na definição do servlet. Os demais foram colocados na configuração da aplicação, dentro das tags <context-param>. Essa tag serve para definir informações específicas da aplicação e não de um servlet ou página JSP em particular. Como o servlet Java tem acesso a esses parâmetros, frequentemente chamados de parâmetros de contexto? Os métodos disponíveis para obter as informações de contexto são muito semelhantes aos utilizados para obter os parâmetros de inicialização específicos de um servlet:

[Servlet].getServletContext()
método da classe Servlet, da qual deriva a classe HttpServlet utilizada para programação web. Retorna um objeto ServletContext que dá acesso aos parâmetros de configuração da aplicação
[ServletContext].getInitParameter("paramètre")
método da classe ServletContext que retorna o valor do parâmetro de inicialização “paramètre”

A classe gener6.java introduz apenas as seguintes alterações no código Java de gener5.java utilizado anteriormente:

             // recuperam-se os parâmetros de inicialização do servlet
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");

            ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //: todos os parâmetros foram recuperados?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // preenche-se a tabela de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
...............

O parâmetro title, específico do servlet, é obtido por meio de um objeto ServletConfig. Os outros três parâmetros definidos no nível da aplicação são obtidos por meio de um objeto ServletContext. Compilamos essa classe e a colocamos, assim como as outras, em <application>\WEB-INF\classes:

Image

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

Image

3.3.6. Parâmetros de inicialização de uma página JSP

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 já estudada:

<%@ page import="java.sql.*, java.util.*" %>

<%!
         // variáveis globais do aplicativo
         // o título da página
        private final String title="Génération d'un formulaire";
         // banco de dados dos valores da lista
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
.........

Encontramos as quatro constantes title, DSNValeurs, admDbValeurs e mdpDbValeurs definidas no arquivo web.xml do aplicativo. As constantes DSNValeurs, admDbValeurs e mdpDbValeurs foram agora definidas no nível do aplicativo e, portanto, podemos supor que uma página JSP que faça parte desse aplicativo terá acesso a elas. É esse o caso. Sabemos que a página JSP será convertida em um servlet. Este terá acesso ao contexto por meio do método getServletContext(). Mais delicado é o caso da constante title. De fato, nós a definimos no nível do servlet e não no nível da aplicação, da seguinte maneira:

    <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 não é mais adequada, pois não existe mais o conceito de arquivo de classe. A sintaxe de configuração de uma página JSP, no entanto, é 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 é considerada como um servlet ao qual se atribui um nome (servlet-name). Em vez de associar um arquivo de classe a esse servlet, associa-se o arquivo-fonte da página JSP a ser executada (jsp-file). Assim, as linhas anteriores definem um servlet chamado JSPlstvaleurs associado à página JSP /listvaleurs2.jsp. O caminho /listvaleurs2.jsp é medido em relação à raiz do aplicativo. Assim, no caso do nosso aplicativo liste, o arquivo listvaleurs2.jsp estaria na pasta docBase (cf. server.xml) da aplicação liste:

Image

A configuração da página JSP será a seguinte no arquivo web.xml do aplicativo:

<?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 na raiz do aplicativo liste e associada ao nome do servlet JSPlstValeurs (servlet-name), que, por sua vez, está associado ao alias /jspvaleurs (servlet-mapping). Assim, nossa página JSP ficará acessível por meio de URL http://localhost:8080/liste/jspvaleurs.

A página inicial JSP, listvaleurs.jsp, é alterada para listvaleurs2.jsp e recupera seus quatro parâmetros de inicialização no método jspInit():

<%!
         // variáveis globais da aplicação
         // o título da página
        private String title=null;
        // o banco de dados dos valores da lista
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // valores da lista
        private String[] valeurs=null;
         // mensagem de erro
        private String msgErreur=null;

         // inicialização da página JSP — executada apenas uma vez
        public void jspInit(){

             // recuperação dos parâmetros de inicialização do servlet
      ServletConfig config=getServletConfig();
            title=config.getInitParameter("JSPtitle");
      ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //: todos os parâmetros foram recuperados?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // preenche a tabela de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
..............

A página JSP recupera seus parâmetros de inicialização da mesma forma que os servlets. O arquivo anterior é salvo na raiz da aplicação web liste:

Image

O servidor Tomcat é reiniciado para forçá-lo a recarregar o novo arquivo de configuração web.xml da aplicação. É então possível acessar o URL http://localhost:8080/liste/jspvaleurs:

Image

3.3.7. Colaboração entre servlets e páginas JSP em uma aplicação web

Quando um cliente faz uma solicitação a um servidor web, a resposta pode ser elaborada por vários servlets e páginas JSP. Até agora, a resposta era elaborada por um único servlet ou página JSP. Vimos que a página JSP proporcionava maior legibilidade à estrutura do documento HTML gerado. No entanto, ela geralmente contém muito código Java. É possível melhorar a situação colocando

  • em um ou mais servlets o código Java que não gera o código HTML da resposta
  • em páginas JSP, o código de geração dos diversos documentos HTML enviados em resposta ao cliente

Dessa forma, podemos esperar melhorar a separação entre o código Java e o código HTML. Vamos aplicar essa nova estrutura à nossa aplicação liste: um servlet Java lstValeurs4 será responsável por ler, no início, os valores do banco de dados e, em seguida, analisar as solicitações dos clientes. Dependendo do resultado dessa análise, a solicitação do cliente será direcionada para uma página de erro erreur.jsp ou para a página de exibição da lista de números liste.jsp. A aplicação liste será, portanto, composta por um servlet e duas páginas JSP.

Como um servlet pode repassar a solicitação que recebeu de um cliente para outro servlet ou para uma página JSP? Utilizaremos os seguintes métodos:

[ServletContext].getRequestDispatcher(
String url)
método da classe ServletContext que retorna um objeto RequestDispatcher. O parâmetro url é o nome do URL para o qual se deseja transmitir a solicitação do cliente. Esse encaminhamento da solicitação só pode ocorrer dentro de uma mesma aplicação. Portanto, o parâmetro url é um caminho relativo à árvore de diretórios da web dessa aplicação.
[RequestDispatcher].forward
(ServletRequest request,
 ServletResponse response)
método da interface RequestDispatcher que transmite ao método anterior URL a solicitação request do cliente e o objeto response que deve ser utilizado para elaborar a resposta.
[ServletRequest].setAttribute(String nom, Object obj)
quando um servlet ou uma página JSP encaminha uma solicitação para outro servlet ou página JSP, geralmente precisa passar para este outras informações além da mera solicitação do cliente, informações resultantes de seu próprio processamento da solicitação. O método setAttribute da classe ServletRequest permite adicionar atributos ao objeto request do cliente de uma forma que se assemelha a um dicionário de pares (atributo, valor), em que attribut é o nome do atributo e valeur é qualquer objeto que represente o valor desse atributo.
[ServletRequest].getAttribute(
String attribut)
permite recuperar os valores dos atributos de uma solicitação. Esse método será utilizado pelo servlet ou pela página JSP, à qual foi encaminhada uma solicitação, para obter as informações que foram adicionadas a ela.

O servlet responsável pelo processamento do formulário será configurado da seguinte maneira no arquivo 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 próprios:

title
o título do documento HTML a ser gerado
JSPerreur
o URL da página de erro JSP
JSPliste
o URL da página JSP que apresenta a lista de números
URLservlet
URL associada ao atributo action do formulário apresentado pela página JSPliste. Essa URL será a do servlet lstValeurs4

A servlet terá o alias /valeurs4 (servlet-mapping) e, portanto, será acessível por meio do URL http://localhost:8080/liste/valeurs4. Ela está vinculada ao arquivo 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{
        // o título da página
        private String title=null;
        // o banco de dados dos valores da lista
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // as páginas de exibição JSP
        private String JSPerreur=null;
        private String JSPliste=null;
        // o URL do servlet
        private String URLservlet=null;
        // valores da lista
        private String[] valeurs=null;
         // mensagem de erro
        private String msgErreur=null;

        // -----------------------------------------------------------------
        // GET
        public void doGet(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // colocamos msgErreur,title nos atributos da solicitação
            request.setAttribute("msgErreur",msgErreur);
            request.setAttribute("title",title);
            request.setAttribute("URLservlet",URLservlet);

             // ocorreu algum erro ao carregar o servlet?
            if(msgErreur!=null){
                 // redirecionamos para uma página de erro JSP
                getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
                 // fim
                return;
            }

             // não ocorreu nenhum erro
             // colocamos a lista de valores nos atributos da solicitação
            request.setAttribute("valeurs",valeurs);

             // recupera-se a eventual escolha do usuário
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";
            request.setAttribute("choix",choix);

             // passa-se o controle para a página JSP de apresentação da lista
            getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
            // fim
            return;
        }//GET

        // -----------------------------------------------------------------
        // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

             // redireciona para GET
            doGet(request,response);
        }//POST

        // -----------------------------------------------------------------
         // inicialização do servlet
        public void init(){

             // recuperação dos parâmetros de inicialização do servlet
            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");


             //todos os parâmetros foram recuperados?
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // preenche a matriz de valores a partir de um banco de dados ODBC
             // com o nome DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 // conexão com o banco de dados ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // objeto Statement
                st=connexion.createStatement();
                 // execução da consulta SELECT para recuperar os valores
                rs=st.executeQuery("select valeur from Tvaleurs");
                // os valores são recuperados e inseridos em uma tabela dinâmica
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    // o valor é registrado na lista
                    lstValeurs.add(rs.getString("valeur"));
                }//while
                 // transformação de lista em tabela
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // problema
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//try
        }//inicializar
    }//classe

A novidade nesta classe é o encaminhamento da solicitação do cliente para a página JSPerreur em caso de erro e para a página JSPliste nos demais casos. A classe não elabora a resposta por conta própria. São as páginas JSP, JSPerreur e JSPliste que se encarregam disso. Anteriormente, o servlet adicionava atributos (setAttribute) à solicitação do cliente:

  • uma mensagem de erro msgErreur em caso de erro na página JSPerreur
  • os valores (valeurs) a serem exibidos, o valor selecionado (choix) pelo usuário, o título (title) do formulário, o URL (URLservlet) do atributo action do formulário para a página JSPliste

Essa classe é compilada e incluída nas classes do aplicativo:

Image

A página JSP, que exibe uma mensagem de erro, está 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 arquivo JSP associado à página de erro se chama erreur.jsp e está localizado na raiz do aplicativo:

Image

Seu alias é /JSPerreur, o que o torna acessível por meio do http://localhost:8080/liste/JSPerreur URL. Ela possui um parâmetro de inicialização chamado mainServlet, cujo valor é o alias do servlet principal descrito anteriormente. Observe-se que esse alias é relativo à raiz da aplicação liste,; caso contrário, teríamos /liste/valeurs4.. O código da página erreur.jsp é o seguinte:

<%
     // código de _jspService
   // recupera-se o parâmetro de inicialização mainServlet
  String servletListValeurs=config.getInitParameter("mainServlet");
   // recupera-se o atributo msgErreur
  String msgErreur=(String)request.getAttribute("msgErreur");
   // o atributo é válido?
  if(msgErreur!=null){
%>
       <!-- código HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %>)</h3>
      </body>
    </html>
<%
    } else { // atributo msgErreur inválido — retorno ao servlet principal
%>
    <jsp:forward page="<%= servletListValeurs %>" />  
<%    
  }
%>  

Essa página normalmente deve ser chamada pelo servlet anterior, que deve passar a ela o atributo msgErreur. No entanto, nada impede que ela seja chamada diretamente, caso se conheça seu URL. Além disso, se for constatado que o atributo msgErreur está ausente, a solicitação é encaminhada para o servlet principal. Aqui, utiliza-se uma tag específica para as páginas JSP, cuja sintaxe é:

<jsp:forward page="URL" />

onde URL é o URL do servlet para o qual a solicitação do cliente é transferida. Se o atributo msgErreur estiver presente, a página de erro é exibida.

A página JSP, que exibe a lista de números, está configurada da seguinte maneira:

    <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 arquivo JSP associado à página de erro se chama liste.jsp e está localizado na raiz do aplicativo:

Image

O alias do servlet é /JSPliste, o que o torna acessível por meio do endereço http://localhost:8080/liste/JSPliste. Ela 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:

   <%-- página de exibição da lista de valores --%>
  <%
       // código de jspService
       // recupera-se o parâmetro de inicialização
      String servletListValeurs=config.getInitParameter("mainServlet");

     // recuperam-se os atributos da solicitação proveniente do servlet principal
    String title=(String) request.getAttribute("title");
    String[] valeurs=(String[]) request.getAttribute("valeurs");
    String choix=(String) request.getAttribute("choix");
    String URLservlet=(String) request.getAttribute("URLservlet");

     // atributos válidos?
    if(title==null || valeurs==null || choix==null){
         // há um atributo inválido — passamos o controle para o servlet
   %>
   <jsp:forward page="<%= servletListValeurs %>" />
    <%
    }//if
  %>

  <%-- código HTML --%>  
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST" action="<%= URLservlet %>">
            <select name="cmbValeurs">
              <%
                 // exibição dinâmica dos valores
                          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>
                <%
             // havia algum valor selecionado?
                    if(! choix.equals("")){
                // exibe-se a escolha do usuário
                %>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
                <%        
                    }//if
                %>        
      </body>
    </html>

Esta página funciona da mesma forma que a página erreur.jsp. Normalmente, ela deve ser chamada pelo servlet /liste/valeurs4 e receber os atributos title, valeurs e choix. Se algum desses parâmetros estiver faltando, o controle é repassado para o servlet URLservlet (/liste/valeurs4). Se todos os parâmetros estiverem presentes, a lista de números é exibida, bem como o número escolhido pelo usuário, caso ele tenha escolhido um.

Se for chamada a servlet principal URL, obtém-se o seguinte resultado:

Image

com o código-fonte a seguir (View/Source):

<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. Percebe-se que os atributos title, valeurs e URLservlet foram efetivamente recuperados.

Para concluir sobre a colaboração entre servlets e páginas JSP, observamos que as páginas JSP são, neste caso, muito curtas e não contêm o código Java que não contribui diretamente para a criação da resposta HTML. A estrutura dos documentos gerados fica, assim, mais visível.

3.4. Ciclo de vida dos servlets e das páginas JSP

3.4.1. O ciclo de vida

Aqui, estamos interessados no ciclo de vida dos servlets. O das páginas JSP decorre dele. Consideremos um servlet chamado pela primeira vez. Uma instância da classe é então criada pelo servidor web e carregada na memória. Ela passará então a atender à solicitação. Feito isso, o servlet não é descarregado da memória. Ela permanece na memória para atender a outras solicitações, a fim de otimizar os tempos de resposta do servidor. Ela será liberada da memória quando tiver decorrido um período de tempo suficientemente longo sem que tenha atendido a novas solicitações. Esse tempo geralmente é configurável no servidor web.

Enquanto estiver na memória, a servlet pode atender a várias solicitações simultaneamente. O servidor web cria um thread por solicitação, e todos utilizam a mesma instância da servlet:

Todas as threads acima compartilham 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 esse assunto.

Ao carregar uma servlet, um método específico da servlet é executado:

public void init() throws ServletException{
}

Para uma página JSP, esse método é


  public void jspInit(){
  }

que é executado. Aqui está um exemplo de página JSP que utiliza o método jspInit:

<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // variáveis e métodos globais da página JSP

   // variável de instância
  int compteur;

   // método para incrementar o contador  
  public int getCompteur(){
     // incrementa-se o contador
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     // o contador é restaurado
    return compteur;
  }

   // o método executado no carregamento inicial da página
  public void jspInit(){
     // inicialização do contador
    compteur=100;
  }
%>

A página anterior JSP inicializa um contador em jspInit com o valor 100. Qualquer solicitação subsequente ao servlet incrementa e, em seguida, exibe o valor desse contador:

Na primeira vez:

Image

Na segunda vez:

Image

Como se pode ver acima, entre as duas solicitações, o servlet não foi liberado; caso contrário, o contador estaria em 101 na segunda solicitação. Quando o servlet é liberado, o método

public void destroy(){
}

é executado, caso exista. Para as páginas JSP, é o método


  public void jspDestroy(){
  }

Nesses métodos, é possível, por exemplo, fechar conexões com bancos de dados, conexões que tenham sido abertas nos métodos init correspondentes.

3.4.2. Sincronização de solicitações com um servlet

Voltemos à página JSP anterior, que incrementa um contador e o retorna ao cliente web. Suponhamos que haja duas solicitações simultâneas. Nesse caso, são criadas duas threads para executá-las, threads que utilizarão a mesma instância do servlet e, portanto, neste caso, o mesmo contador. Recordemos o código que incrementa o contador:


  public int getCompteur(){
     // incrementa o contador
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     // retornando o valor
    return compteur;
  }

O incremento do contador foi escrito propositalmente de forma desajeitada. Suponhamos que a execução das duas threads ocorra da seguinte maneira:

 
  1. no momento T1, a thread TH1 é executada. Ela lê o contador (=145) em myCompteur, depois é interrompida e perde o controle do processador. Portanto, ele não teve tempo de incrementar myCompteur e copiar o novo valor para compteur.
  2. No momento em que T2 é executado, a thread TH2 está em execução. Ela lê o contador (=145) em myCompteur, depois é interrompida e perde o processador. Observe-se que as duas threads possuem variáveis myCompteur diferentes. Elas compartilham apenas as variáveis de instância, aquelas que são globais aos métodos.
  3. No momento T3, a thread TH1 retoma o controle e é concluída. Assim, ela retorna 146 ao seu cliente.
  4. No momento T4, o thread TH2 retoma o controle e termina. Ele também retorna 146 ao seu cliente, quando deveria ter retornado 147.

Temos aqui um problema de sincronização de threads. Quando TH1 deseja incrementar o contador, seria necessário impedir que qualquer outra thread fizesse o mesmo. Para destacar esse problema, reescrevemos a página JSP da seguinte maneira:

<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // variáveis e métodos globais da página JSP

   // variável de instância
  int compteur;

   // método para incrementar o contador  
  public int getCompteur(){
     // lê-se o contador
    int myCompteur=compteur;
     // pausa de 10 segundos
    try{
      Thread.sleep(10000);
    }catch (Exception ignored){}
     // incrementa-se o contador
    compteur=myCompteur+1;
     // retornar o valor
    return compteur;
  }

   // o método executado no carregamento inicial da página
  public void jspInit(){
     // inicializar contador
    compteur=100;
  }
%>

Aqui, forçamos a thread a parar 10 segundos após ler o contador. Assim, ela deve liberar o processador, permitindo que outra thread leia, por sua vez, um contador que não foi incrementado. Quando fazemos consultas com um navegador, não percebemos nenhuma diferença, exceto pela espera de 10 segundos antes de obter o resultado.

Image

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

Image

Image

Obtemos o mesmo valor do contador. Podemos destacar melhor o problema com um cliente programado, em vez de um manual como o navegador. Segue um cliente em Perl que é chamado da seguinte maneira:

programa URL N

onde

URL é o URL do servlet de contagem

N é o número de solicitações a serem feitas a essa servlet

Aqui estão os resultados obtidos para 5 solicitações, que demonstram claramente o problema de sincronização incorreta das threads: todas obtêm 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){

         // dados
        String syntaxe="Syntaxe : pg URL nbAppels";

         // verificação dos parâmetros
        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
         // número de chamadas
        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

         // os parâmetros estão corretos — é possível estabelecer as conexões com o 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
    }//main

    private static void getCompteurs (URL urlCompteur, int nbAppels)
            throws Exception {
         // executa nbAppels no URL urlCompteur
         // exibe sempre o valor do contador retornado pelo servidor web


         // retira do urlCompteur as informações necessárias para a conexão com o servidor da Receita Federal
        String path=urlCompteur.getPath();
        if(path.equals("")) path="/";
        String host=urlCompteur.getHost();
        int port=urlCompteur.getPort();
        if(port==-1) port=urlCompteur.getDefaultPort();

         // são feitas as chamadas para o URL
        Socket[] clients=new Socket[nbAppels];
        for(int i=0;i<nbAppels;i++){
             // conecta-se ao servidor
            clients[i]=new Socket(host,port);
             // cria-se um fluxo de gravação para o servidor
            PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
             // solicita-se o URL — envio dos cabeçalhos HTTP
            OUT.println("GET " + path + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            OUT.println("Connection: close");
            OUT.println("");
        }//para

         // dados locais
        String réponse=null;                        // resposta do servidor
         // o modelo procurado na resposta HTML do servidor
        Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
         // o modelo de uma resposta correta
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
         // o resultado da comparação com o modelo
        Matcher résultat=null;

        for(int i=0;i<nbAppels;i++){
             // cada cliente lê a resposta enviada pelo servidor

             // criam-se os fluxos de entrada e saída do cliente TCP
            BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));

             // lê-se a primeira linha da resposta
            réponse=IN.readLine();
             // compara-se a linha HTTP com o modelo da resposta correta
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                 // ocorre um problema com URL
                throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
            }//se

             // lê-se a resposta até o final dos cabeçalhos
            while((réponse=IN.readLine())!=null && ! réponse.equals("")){
            }//enquanto

             // os cabeçalhos terminaram HTTP — passamos para o código HTML
             // para recuperar o valor do contador
            boolean compteurTrouvé=false;
            while((réponse=IN.readLine())!=null){
                 // comparamos a linha com o modelo do contador
                if(! compteurTrouvé){
                    résultat=modèleCompteur.matcher(réponse);
                    if(résultat.find()){
                         // contador encontrado
                        System.out.println("Compteur="+résultat.group(1));
                        compteurTrouvé=true;
                    }//se
                }//se
            }//enquanto

             // concluído
            clients[i].close();
        }//for
    }//getCompteurs

}//classe

Vamos explicar o código anterior:

  • o programa aceita dois parâmetros:
    • o URL da página JSP do contador
    • o número de clientes a serem criados para essa URL
  • o programa começa, portanto, verificando a validade dos parâmetros: se há de fato dois, se o primeiro se assemelha sintaticamente a um URL e se o segundo a um número inteiro >0. Para verificar se o URL está sintaticamente correto, utiliza-se a classe URL e seu construtor URL (String), que cria um objeto URL a partir de uma sequência de caracteres como http://istia.univ-angers.fr. É lançada uma exceção se a sequência de caracteres não for um URL sintaticamente válido. Isso nos permite verificar a validade do primeiro parâmetro.
  • Após a verificação dos parâmetros, o controle é passado para a procedimento getCompteurs. Este procedimento criará clientes nbAppels, os quais se conectarão todos ao mesmo tempo (ou quase) ao URL urlCompteur.
  • A porta e a máquina às quais os clientes devem se conectar são obtidas do URL urlCompteur: [URL].getHost() permite obter o nome do servidor e [URL].getPort() permite obter a porta.
  • Um primeiro loop permite que cada cliente:
    • conectar-se ao servidor web
    • solicitar a ele o URL urlCompteur

Nesse ciclo, o cliente não aguarda a resposta do servidor. De fato, o objetivo é fazer com que o servidor receba solicitações quase simultâneas.

  • Um segundo loop permite que cada cliente receba e processe a resposta enviada pelo servidor. O processamento consiste em localizar na resposta a linha que contém o valor do contador e exibi-la.

Para resolver o problema apontado anteriormente (o mesmo contador enviado aos cinco clientes), precisamos sincronizar as threads do serviço de contagem em um mesmo objeto antes de entrar na seção crítica de leitura e atualização do contador. A nova página JSP é a seguinte:


<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // variáveis e métodos globais da página JSP
  
   // variáveis de instância
  int compteur;
  Object verrou=new Object();
  
   // método para incrementar o contador  
  public int getCompteur(){
  
     // sincroniza-se a seção crítica
    synchronized(verrou){
       // lê-se o contador
      int myCompteur=compteur;
       // pausa de 10 segundos
      try{
        Thread.sleep(10000);
      }catch (Exception ignored){}
       // incrementa-se o contador
      compteur=myCompteur+1;
    }//sincronizado
     // retornando
    return compteur;
  }//getCompteur

   // o método executado no carregamento inicial da página
  public void jspInit(){
     // inicialização do contador
    compteur=100;
  }
%>

Ao executar, obtêm-se então os seguintes resultados:

dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/contador/compteur3.jsp 5
    Compteur= 104
    Compteur= 106
    Compteur= 105
    Compteur= 107
    Compteur= 108

A documentação indica que o servidor web pode, às vezes, criar várias instâncias de um mesmo servlet. Nesse caso, a sincronização anterior deixa de funcionar, pois a variável verrou é local a uma instância e, portanto, não é reconhecida pelas outras instâncias. O mesmo se aplica à variável compteur. Para torná-las globais para todas as instâncias, escreveremos:


// variável de classe
  static int compteur;
  static Object verrou=new Object();

O restante do código permanece inalterado.