Erro ao alterar contato no exercício 9.11 da apostila Caelum

Estou tentando aprender a desenvolver aplicações Java para WEB (JavaEE). Para isso estava utilizando uma apostila confeccionada pela equipe da Caelum. Tudo ia bem até o momento em que resolvi fazer o exercício do item 9.8 da mesma o qual pedia para desenvolver uma lógica para alterar e inserir contato. Comecei, como sugeria a apostila, inserindo um link para alterar contato na página que lista os contatos conforme mostra o código abaixo:

Arquivo: listaContatos.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<jsp:useBean id="contato" class="agenda.Contato" scope="session"/>
<jsp:setProperty name="contato" property="*"/>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>teste</title>


<title>Listar contatos</title>
</head>
<body>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<c:import url="cabecalho.jsp" />


<table border="1px">
    <thead>
        <tr><th>NOME</th><th>EMAIL</th><th>ENDEREÇO</th><th>DATA NASCIMENTO</th></tr>
    </thead>
    <c:forEach var="contato" items="${contatos}" varStatus="id">
        <tr bgcolor="#${id.count % 2 == 0 ? 'aaee88' : 'ffffff' }" >
            <td>${contato.nome}</td>
            <td>
                <c:choose>
                    <c:when test="${not empty contato.email}">
                        <a href="mailto:${contato.email}">${contato.email}</a>
                    </c:when>
                    <c:otherwise>
                        Não informado
                    </c:otherwise>
                </c:choose>
            </td>
            <td>${contato.endereco}</td>
            <td>
                <fmt:formatDate value="${contato.dataNascimento.time}" 
                pattern="dd/MM/yyyy" />
            </td>
            
            <td>
                <!-- Aqui Link para a lógica para alterar contato -->
                <a href="mvc?logica=AlteraContatoLogic&id=${contato.id}">Alterar</a>
            </td>
            
            <td>
                <a href="mvc?logica=RemoveContatoLogic&id=${contato.id}">Remover</a>
            </td>
            
        </tr>
    </c:forEach>
</table>

<c:import url="rodape.jsp" />

</body>
</html>`

A seguir, a lógica para alterar contato. Arquivo: AlteraContatoLogic.java (para onde o link da página de listar contatos aponta).

package logica;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import agenda.Contato;
import agenda.ContatoDAO;

public class AlteraContatoLogic implements Logica {

    @Override
    public String executa(HttpServletRequest req, HttpServletResponse res) throws Exception {
        long id = Long.parseLong(req.getParameter("id"));
        ContatoDAO dao = new ContatoDAO();
        Contato contato = new Contato();
        contato = dao.getContato(id);
        
        req.setAttribute("contato", contato);
        
        return "altera_contato.jsp";    
        
    }
    

}

A seguir Logica.java (a interface)

package logica;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface Logica {
    
    String executa(HttpServletRequest req,
            HttpServletResponse res) throws Exception;

}

Página com o formulário para alterar contato: altera_contato.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

<jsp:useBean id="contato" class="agenda.Contato" scope="session"/>
<jsp:setProperty name="contato" property="*" />

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>

<script src="/js/jquery.maskedinput.js"></script>
</head>

<body>

<c:import url="cabecalho.jsp" />

<hr>
<h3>Alterar Contato</h3>

<form action="mvc">
    <input type="hidden" name="logica" value="AlteraContatoLogic2">
    <input type="hidden" name=id value="${contato.id}">
    <table>
        <tr>
            <td>Nome:</td><td><input type="text" name="nome" value="${contato.nome }"/></td>
        </tr>
        <tr>
            <td>E-mail:</td><td><input type="text" name="email" value="${contato.email }"/></td>
        </tr>
        <tr>
            <td>Endereço:</td><td><input type="text" name="endereco" value="${contato.endereco}"/></td>
        </tr>
        <tr>
            <td>Data Nascimento:</td>
            <td>
                <input type="text" name="dataNascimento" value='<fmt:formatDate value="${contato.dataNascimento.time }"/>'>                
            </td>
        </tr>
    </table>

    <hr>
    
    <input type="submit" value="Gravar" />
</form>

<c:import url="rodape.jsp" />

</body>
</html>

Logica para alterar contato no banco de dados: AlteraContatoLogic2.java

package logica;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import agenda.Contato;
import agenda.ContatoDAO;

public class AlteraContatoLogic2 implements Logica {

    @Override
    public String executa(HttpServletRequest req, HttpServletResponse res) throws Exception {
        
        try {
            long id = Long.parseLong(req.getParameter("id"));
            String nome = req.getParameter("nome");
            String email = req.getParameter("email");
            String endereco = req.getParameter("endereco");
            String strData = req.getParameter("dataNascimento");
            
            Date date = new SimpleDateFormat("dd/MM/yyyy").parse(strData);
            Calendar dataNascimento = Calendar.getInstance();
            dataNascimento.setTime(date);
            
            Contato contato = new Contato();
            contato.setId(id);
            contato.setNome(nome);
            contato.setEmail(email);
            contato.setEndereco(endereco);
            contato.setDataNascimento(dataNascimento);
            
            ContatoDAO dao = new ContatoDAO();
            dao.altera(contato);
            System.out.println("Alterar contato... ");
            
            //Creio ser este o valor de retorno a ser passado para o controler.
            return "mvc?logica=ListaContatosLogic";
            
        } catch (ParseException e) {
            e.printStackTrace();
            return "erro.html"; //para a execução do método
        }
        
        
    }
    

}


Acho que não é necessário postar “ConectionFactory.java”, “Contato.java” e “ContatoDAO.java”. Basta dizer que estão funcionando.

Por fim, o problema que gostaria de submeter a vossa análise: quando “clico” em Gravar uma exception é gerada no controler (creio eu). Vide mensagem de erro abaixo trancrita:


type Exception reportmessage A lógica de negócios causou uma exceção para AlteraContatoLogic2description The server encountered an internal error that prevented it from fulfilling this request.exceptionjavax.servlet.ServletException: A lógica de negócios causou uma exceção para AlteraContatoLogic2
	mvc.ControllerServlet.service(ControllerServlet.java:37)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root causejavax.servlet.ServletException: A lógica de negócios causou uma exceção para ListaContatosLogic
	mvc.ControllerServlet.service(ControllerServlet.java:37)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root causeorg.apache.jasper.JasperException: org.apache.jasper.JasperException: org.apache.jasper.JasperException: org.apache.jasper.JasperException: Unable to convert string "24/02/2011" to class "java.util.Calendar" for attribute "dataNascimento": Property Editor not registered with the PropertyEditorManager
	org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:555)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:461)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root causeorg.apache.jasper.JasperException: org.apache.jasper.JasperException: org.apache.jasper.JasperException: Unable to convert string "24/02/2011" to class "java.util.Calendar" for attribute "dataNascimento": Property Editor not registered with the PropertyEditorManager
	org.apache.jasper.runtime.JspRuntimeLibrary.internalIntrospecthelper(JspRuntimeLibrary.java:362)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(JspRuntimeLibrary.java:308)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspect(JspRuntimeLibrary.java:286)
	org.apache.jsp.listaContatos_jsp._jspService(listaContatos_jsp.java:146)
	org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root causeorg.apache.jasper.JasperException: org.apache.jasper.JasperException: Unable to convert string "24/02/2011" to class "java.util.Calendar" for attribute "dataNascimento": Property Editor not registered with the PropertyEditorManager
	org.apache.jasper.runtime.JspRuntimeLibrary.convert(JspRuntimeLibrary.java:273)
	org.apache.jasper.runtime.JspRuntimeLibrary.internalIntrospecthelper(JspRuntimeLibrary.java:354)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(JspRuntimeLibrary.java:308)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspect(JspRuntimeLibrary.java:286)
	org.apache.jsp.listaContatos_jsp._jspService(listaContatos_jsp.java:146)
	org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root causeorg.apache.jasper.JasperException: Unable to convert string "24/02/2011" to class "java.util.Calendar" for attribute "dataNascimento": Property Editor not registered with the PropertyEditorManager
	org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager(JspRuntimeLibrary.java:854)
	org.apache.jasper.runtime.JspRuntimeLibrary.convert(JspRuntimeLibrary.java:269)
	org.apache.jasper.runtime.JspRuntimeLibrary.internalIntrospecthelper(JspRuntimeLibrary.java:354)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(JspRuntimeLibrary.java:308)
	org.apache.jasper.runtime.JspRuntimeLibrary.introspect(JspRuntimeLibrary.java:286)
	org.apache.jsp.listaContatos_jsp._jspService(listaContatos_jsp.java:146)
	org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	mvc.ControllerServlet.service(ControllerServlet.java:35)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
note The full stack trace of the root cause is available in the Apache Tomcat/8.0.36 logs.A

Organização das pastas (IDE: Eclipse)

.
├── ./build
│   └── ./build/classes
│       ├── ./build/classes/agenda
│       │   ├── ./build/classes/agenda/ConnectionFactory.class
│       │   ├── ./build/classes/agenda/Contato.class
│       │   └── ./build/classes/agenda/ContatoDAO.class
│       ├── ./build/classes/logica
│       │   ├── ./build/classes/logica/AlteraContatoLogic2.class
│       │   ├── ./build/classes/logica/AlteraContatoLogic3.class
│       │   ├── ./build/classes/logica/AlteraContatoLogic.class
│       │   ├── ./build/classes/logica/ListaContatosLogic.class
│       │   ├── ./build/classes/logica/Logica.class
│       │   └── ./build/classes/logica/RemoveContatoLogic.class
│       └── ./build/classes/mvc
│           └── ./build/classes/mvc/ControllerServlet.class
├── ./src
│   ├── ./src/agenda
│   │   ├── ./src/agenda/ConnectionFactory.java
│   │   ├── ./src/agenda/ContatoDAO.java
│   │   └── ./src/agenda/Contato.java
│   ├── ./src/logica
│   │   ├── ./src/logica/AlteraContatoLogic2.java
│   │   ├── ./src/logica/AlteraContatoLogic3.java
│   │   ├── ./src/logica/AlteraContatoLogic.java
│   │   ├── ./src/logica/ListaContatosLogic.java
│   │   ├── ./src/logica/Logica.java
│   │   └── ./src/logica/RemoveContatoLogic.java
│   └── ./src/mvc
│       └── ./src/mvc/ControllerServlet.java
└── ./WebContent
    ├── ./WebContent/AdicionaContato.jsp
    ├── ./WebContent/altera_contato.jsp
    ├── ./WebContent/cabecalho.jsp
    ├── ./WebContent/css
    │   └── ./WebContent/css/jquery-ui.css
    ├── ./WebContent/documentos
    │   ├── ./WebContent/documentos/aniversariantes2.csv
    │   ├── ./WebContent/documentos/aniversariantes.csv
    │   └── ./WebContent/documentos/contatos.csv
    ├── ./WebContent/erro.html
    ├── ./WebContent/imagens
    │   └── ./WebContent/imagens/caelum.png
    ├── ./WebContent/index.html
    ├── ./WebContent/js
    │   ├── ./WebContent/js/jquery-3.0.0.min.js
    │   ├── ./WebContent/js/jquery.maskedinput.js
    │   ├── ./WebContent/js/jquery.ui.datepicker-pt-BR.js
    │   ├── ./WebContent/js/jquery-ui.js
    │   └── ./WebContent/js/jquery-ui.min.js
    ├── ./WebContent/listaContatos.jsp
    ├── ./WebContent/META-INF
    │   └── ./WebContent/META-INF/MANIFEST.MF
    ├── ./WebContent/rodape.jsp
    └── ./WebContent/WEB-INF
        ├── ./WebContent/WEB-INF/lib
        │   ├── ./WebContent/WEB-INF/lib/javax.servlet.jsp.jstl-api-1.2.1.jar
        │   ├── ./WebContent/WEB-INF/lib/jstl-1.2.jar
        │   ├── ./WebContent/WEB-INF/lib/postgresql-9.3-1102.jdbc41.jar
        │   └── ./WebContent/WEB-INF/lib/standard-1.1.2.jar
        └── ./WebContent/WEB-INF/web.xml

17 directories, 43 files

Algumas informações adicionais:

  1. Há uma linha no stack informando que não consegue converter converter String para Java.util.calendar. Não entendo como isso ocorre.
  2. A atualização no banco de dados, apesar da mensagem de erro, é executada. Verifiquei isso quando recarreguei a página listarContatos.jsp. Os campos que editei reapareceram alterados como esperado.
  3. Estou usando Apache Tomcat/8.0.36.
  4. Pela mensagem e após tentar depurar a aplicação (modo debug) verifiquei que a exceção ocorre no controler quando recebe o parámetro logica=ListarContatosLogic.
  5. Se houver necessidade, posso enviar todo o código compactado e pronto para ser importado no Eclipse.

Finalizando. Já estou há dias tentando encontrar a causa desse erro, mas sem sucesso. Talvez alguém com mais conhecimento possa enxergar a causa do mesmo.
Desde já agradeço.

Boa noite,

Você observou essa linha no seu stack trace: root causeorg.apache.jasper.JasperException: Unable to convert string “24/02/2011” to class “java.util.Calendar” for attribute “dataNascimento”: Property Editor not registered with the PropertyEditorManager

Corrija esse erro e debug sua aplicação, você sabe debugar?

Sim. Acho que sei. E foi justamente assim que percebi que a exception ocorre ao executar ControllerServlet com a variável pagina com o valor "mvc?logica=ListaContatosLogic. Mais precisamente quando tenta executar

request.getRequestDispatcher(pagina).forward(request, response);

Eu acreditava que após alterar o contato no banco de dados com AlteraContatoLogic2/executa, implementação da classe Logica para fazer o update da tabela contatos cujos parâmetros foram passados no formulário de altera_contato.jsp. Então, a meu ver, essas variáveis, a saber: id, nome, endereco, email e dataNascimento que foram passadas, a princípio, para AlteraContatoLogic2/executa para fazer a atualização do contato também foram passadas para listaContatos.jsp. É possível que a expressão:

<jsp:useBean id="contato" class="agenda.Contato" scope="session"/>
<jsp:setProperty name="contato" property="*"/>

em listaContatos.jsp seja a fonte do erro? Pois aí a var contato estaria esperando um valor do tipo java.util.calendar para a data e não uma String. Mas disso eu não tenho certeza.
Pergunto se alguém não teria esse exercício já pronto para que eu possa tentar entender a maneira correta de desenvolver esse programa.
Obrigado pela atenção.

Acho que encontrei uma solução para esse problema. Ao invés de tentar redirecionar imediatamente para para a página de listar contatos, redirecionei para uma página informando o sucesso a operação, esta com um botão ok o qual redireciona para listar contatos.
Não sei se foi a melhor solução, mas resolveu.