Java Servelet na prática

Com a introdução de novos conceitos a partir do Java EE 6 e agora consolidando esses conceitos e novidades do Java EE 7 temos mais produtividade e facilidade para desenvolver aplicações web utilizando Servlets. A quantidade de configurações diminuiu consideravelmente em relação às últimas versões da plataforma. Veremos como podemos desenvolver Servlets de uma maneira muito mais simples com o WebServlet.

Nas próximas seções veremos melhor para que serve um WebServlet, como podemos defini-lo e como podemos utiliza-lo em nossas aplicações. Também veremos outras características dos servlets e algumas alternativas para definição de Servlets.

WebServlet

Um servlet pode ser definido utilizando a anotação @WebServlet em uma classe POJO, além disso devemos também estender a classe javax.servlet.http.HttpServlet.

Segue na Listagem 1 um exemplo de uma definição de um Servlet utilizando a nova anotação @WebServlet.

LIstagem 1. Exemplo de definição de um servlet com @WebServlet.

    @WebServlet("/teste")
    public class TesteServlet extends javax.servlet.http.HttpServlet {
             //. . .
    }  

O nome da classe totalmente qualificado é o nome padrão do Servlet. Este nome pode ser substituído usando o atributo “name” da anotação.

O Servlet também pode ser publicado (deployed) em múltiplas URLs. Segue na Listagem 2 um exemplo de como poderíamos fazer isto.

LIstagem 2. Exemplo de como podemos definir um servlet para múltiplas URLs.


    @WebServlet(urlPatterns={"/teste", "/testeServlet"})
    public class TesteServlet extends javax.servlet.http.HttpServlet {
             //. . .
    }  

Também podemos utilizar uma anotação @WebInitParam para especificar um parâmetro de inicialização, conforme ilustra o código da Listagem 3.

LIstagem 3. Utilizando @WebInitParam

    @WebServlet(urlPatterns="/teste", initParams={
     @WebInitParam(name="tipo", value="testeDeValor")})
    public class TesteServlet extends javax.servlet.http.HttpServlet {
             //. . .
    }  

A interface HttpServlet tem um método doXXX, onde XXX pode ser HTTP GET, POST, PUT, DELETE, HEAD, OPTIONS, ou TRACE. Esses métodos podem ser utilizados para manipular requisições.

Segue um exemplo na Listagem 4.

LIstagem 4. Exemplo utilizando o método doGet para manipular requisições do tipo GET.

    @WebServlet("/teste")
    public class TesteServlet extends javax.servlet.http.HttpServlet {
             @Override
             protected void doGet(HttpServletRequest request, 
             HttpServletResponse response) {
                       //. . .
             }
    }  

Neste código o HttpServletRequest e o HttpServletResponse definidos como parâmetros do método capturam solicitações e respostas com o cliente web. Os parâmetros de solicitação, cabeçalhos do HTTP, diferentes partes do caminho tais como host, portas, contextos e outras informações estão disponíveis através do HttpServletRequest.

Os cookies HTTP também podem ser enviados e recuperados. O desenvolvedor é responsável por popular o HttpServletResponse. O container, em seguida, transmite os cabeçalhos HTTP capturados e/ou o corpo da mensagem para o cliente.

O código da Listagem 5 mostra como um pedido HTTP GET recebido por um Servlet exibe uma resposta simples de volta para o cliente.

LIstagem 5. Exemplo de como retornar uma resposta ao cliente.

    protected void doGet(HttpServletRequest request, 
     HttpServletResponse response) {
             try (PrintWriter out = response.getWriter()) {
                       out.println("<html><head>");
                       out.println("<title>Meu Servlet Exemplo</title>");
                       out.println("</head><body>");
                       out.println("<h1>Minha Resposta Usando Servlet</h1>");
                       //. . .
                       out.println("</body></html>");
             } finally {
                       //. . .
             }
    }  

Os parâmetros de solicitação podem ser passados através de requisições GET e POST. Em uma requisição GET, esses parâmetros são passados na string de consulta como pares nome/valor.

Segue um exemplo de uma URL invocando um servlet com parâmetros de requisição: http://localhost:8080/NomeAplicacao/teste?tx=10

O “teste” seria o nome do servlet enquanto que “tx” é o parâmetro e “10” é o valor do parâmetro sendo passado para o Servlet.

Em requisições POST, os parâmetros das requisições podem ser passados nos dados enviados que estão codificados no corpo da requisição. Em ambas as requisições POST e GET, esses parâmetros podem ser obtidos através de um HttpServletRequest.

Segue um exemplo de código na Listagem 6 mostrando como podemos

recuperar o parâmetro da URL anterior.

LIstagem 6. Exemplo de como podemos recuperar um parâmetro enviado em uma URL.

    protected void doGet(HttpServletRequest request, 
     HttpServletResponse response) {
             String txValor = request.getParameter("tx");
             //. . .
    }  

Parâmetros de inicialização podem ser definidos em um Servlet para que possamos armazenar informações de inicialização e configuração. Para isso é utilizada a anotação @WebInitParam conforme explicado anteriormente.

Segue um exemplo na Listagem 7 de como obter um parâmetro de inicialização.

LIstagem 7. Exemplo de como podemos recuperar parâmetros de inicialização.

    String tipo = null;
    @Override
    public void init(ServletConfig config) throws ServletException {
             tipo = config.getInitParameter("tipo");
             //. . .
    }  

Também podemos manipular o comportamento padrão dos métodos do ciclo de vida do Servlet. Para isso basta substituirmos os métodos init, service e destroy da interface javax.servlet.Servlet.

Normalmente, conexões com a base de dados são inicializadas no init e liberadas no destroy.

Outra forma de definirmos um servlet além da anotação @WebServlet é através dos elementos servlet e servlet-mapping no deployment descriptor da aplicação web, ou seja, no arquivo web.xml. Podemos definir o Servlet “Teste” usando o web.xml, conforme a Listagem 8.

LIstagem 8. Exemplo definindo um Servlet com web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.1"
             xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
             <servlet>
                <servlet-name>TesteServlet</servlet-name>
                <servlet-class>com.exemplo.TesteServlet</servlet-class>
             </servlet>
     
             <servlet-mapping>
                       <servlet-name>TesteServlet</servlet-name>
                       <url-pattern>/teste</url-pattern>
             </servlet-mapping>
    </web-app>  

Como as anotações cobrem a maioria dos casos, o arquivo web.xml não é obrigatório. Porém em alguns casos como a ordenação de servlets, podemos fazer isto apenas com arquivo web.xml. Se o elemento "metadata-complete" estiver configurado para “true” no arquivo web.xml, então as anotações nas classes não serão processadas.

Segue abaixo um exemplo na Listagem 9.

LIstagem 9. Exemplo de como definer o atributo metadata-complete.

    <web-app version="3.1"
             xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             metadata-complete="true">
     
             //. . .
     
    </web-app>  

Os valores definidos no deployment descriptor sobrescrevem os valores definidos usando anotações.

Um Servlet é empacotado numa aplicação web em um arquivo war. Múltiplos servlets podem ser empacotados juntos, e todos eles compartilham um contexto de Servlet. O ServletContext fornece detalhes sobre o ambiente de execução dos Servlets e é usado para se comunicar com o container para ler um recurso na aplicação web, escrever em um arquivo de log ou despachar uma requisição.

O ServletContext pode ser obtido através de um HttpServletRequest. Segue na Listagem 10 um exemplo.

LIstagem 10. Exemplo de como podemos adquirir um ServletContext.

    protected void doGet(HttpServletRequest request, 
     HttpServletResponse response) {
             ServletContext context = request.getServletContext();
             
             //. . .
     
    }  

Os Servlets também suportam a definição de cookies. Um Servlet pode enviar um cookie HTTP, chamado JSESSIONID, ao cliente para rastreamento de sessão.

Este cookie pode ser marcado como “HttpOnly”, onde assegura-se que o cookie não está exposto ao código de scripting do lado cliente, e dessa forma, ajuda a reduzir certos tipos de ataques de script. Segue na Listagem 11 um exemplo.

LIstagem 11. Exemplo de como podemos tornar nossa aplicação mais segura com cookies.

    SessionCookieConfig config = request.getServletContext().getSessionCookieConfig();
    config.setHttpOnly(true);  

Alternativamente, a reescrita de URL pode ser utilizada por um Servlet como uma base para o rastreamento de sessão. O método ServletContext.getSessionCookieConfig retorna um SessionCookieConfig, que pode ser usado para configurar diferentes propriedades do cookie.

Uma interface HttpSession pode ser utilizada para visualizar e manipular informações sobre uma sessão, como o identificador de sessão e hora de criação, e para vincular objetos à sessão.

Um novo objeto de sessão pode ser criado conforme indica o código da Listagem 12.

LIstagem 12. Exemplo de como criar um novo objeto de sessão.

    protected void doGet(HttpServletRequest request, 
     HttpServletResponse response) {
             HttpSession session = request.getSession(true);
     
             //. . .
       }  

Os métodos “session.setAttribute()” e “session.getAttribute()” são usados para ligar objetos com a sessão.

Servlets também suportam o encaminhamento de solicitações para outro Servlet quando um processamento adicional for necessário.

Para isso podemos enviar uma solicitação para um recurso diferente usando um objeto “RequestDispatcher”, que pode ser obtido a partir de um “HttpServletRequest.getRequestDispatcher” ou “ServletContext.getRequestDispatcher”. O primeiro pode aceitar um caminho relativo, enquanto que o segundo pode aceitar um caminho relativo apenas ao contexto atual. Segue na Listagem 13 um exemplo.

LIstagem 13. Exemplo de encaminhamento para outro Servlet.

    protected void doGet(HttpServletRequest request, 
      HttpServletResponse response) {
           request.getRequestDispatcher
          ("testeOutroServlet").forward(request, response);
     
             //. . .
     
    }  

Neste código, testeOutroServlet é outro Servlet publicado (deployed) no mesmo contexto. O método ServletContext.getContext pode ser usado para obter um ServletContext para contextos externos. Ele pode então ser utilizado para obter um RequestDispatcher, o qual pode enviar pedidos nesse contexto.

Também podemos redirecionar a resposta de um Servlet para outro recurso chamando o método “HttpServletResponse.sendRedirect”. Este método envia uma resposta de redirecionamento temporário para o cliente, e o cliente emite uma nova solicitação para a URL especificada. Pode-se notar que neste caso, o objeto de requisição original não está disponível para a URL redirecionada. O redirecionamento também podem ser mais lento, porque implica em duas solicitações do cliente, enquanto que o forward é realizado dentro do container.

Segue um exemplo na Listagem 14.

LIstagem 14. Exemplo de redirecionamento utilizando senRedirect.

    protected void doGet(HttpServletRequest request, 
     HttpServletResponse response) {
     
             //. . .
     
             response.sendRedirect("http://outroSite.com/AlgumOutroServlet");
     
    }  

Neste exemplo a resposta é redirecionada para a URL http://outroSite.com/AlgumOutroServlet. Pode-se notar que esta URL pode estar em um host / porta diferente e pode ser relativa ou absoluta para o container.

Além de declarar Servlets usando @WebServlet e web.xml, também podemos defini-los por meio de programação usando métodos “ServletContext.addServlet”. Podemos fazer isto através do método “ServletContainerInitializer.onStartup” ou “ServletContextListener.contextInitialized”. O método “ServletContainerInitializer.onStartup” é invocado quando a aplicação está iniciando para um certo ServletContext. O método “addServlet” retorna “ServletRegistration.Dynamic”, que pode ser usado para criar o mapeamento de URL, configurar papéis de segurança, configurar parâmetros de inicialização, e gerenciar outros itens de configuração. Segue na Listagem 15 um exemplo.

LIstagem 15. Adicionando Servlet utilizando addServlet.

    public class MeuInicializador implements ServletContainerInitializer {
     
             @Override
             public void onStartup (Set<Class<?>> 
            clazz, ServletContext context) {
                       ServletRegistration.Dynamic reg =
                                context.addServlet("MeuServlet", 
                                "com.exemplo.MeuServlet");
                       reg.addMapping("/meuServlet");
             }
       }  

Neste artigo vimos como podemos definir Servlet utilizando a anotação @WebServlet e como podemos definir outras configurações através de anotações. Também vimos como isso era feito através do arquivo web.xml e uma outra alternativa a esses dois em que define-se um servlet programaticamente. Além disso, vimos outras configurações e características importantes dos Servlets.

Bibliografia

[1]Josh Juneau. Java EE 7 Recipes: A Problem-Solution Approach. Apress, 2013.

[2]Josh Juneau. Introducing Java EE 7: A Look at What's New. Apress, 2013.

[3]Arun Gupta. Java EE 7 Essentials. O'Reilly, 2013.

Links Úteis

  • Java 7:
    Site com informações sobre o lançamento do Java 7
  • JavaFX:
    Site para fazer download de aplicações JavaFX
  • JFXtras:
    Site do projeto JFXtras

Saiba mais sobre Java ;)

  • O Que é JPA?:
    Dominar a persistência de dados é uma necessidade indispensável aos programadores. Sem esse conhecimento nossas aplicações não terão a capacidade de armazenar e recuperar os dados por ela manipulados.
  • Preparando o ambiente para programar em Java:
    Neste curso você aprenderá a preparar seu ambiente para programar em Java. Veremos aqui o que é necessário instalar e como proceder para desenvolver aplicações com essa linguagem.
  • Criando meu primeiro projeto no Java:
    Neste curso você aprenderá a criar o seu primeiro programa com Java, e não, ele não será um simples “Hello, World!”. :) Para isso, vamos começar ensinando como instalar o Java e preparar o ambiente de desenvolvimento.