listaTarefas Caelum-Java-Web

Pessoal, boa noite.
Pesquisei no fórum acerca do problema que estou enfrentando, porém não obtive sucesso quanto a resolução do mesmo.

De acordo com a apostila da Caelum java-web-fj21, estou tentando fazer o exercício 11.14.

Ao tentar acessar a listagem das tarefas cadastradas, a mesma apresenta erro.

De acordo com o Log, estou com o seguinte erro:
127.0.0.1 - - [06/Jun/2014:23:43:54 -0300] “GET /fj21-tarefas/listaTarefas HTTP/1.1” 500 3680

Segue também a página de erro apresentada:

HTTP Status 500 - Request processing failed; nested exception is java.lang.NullPointerException

type Exception report

message Request processing failed; nested exception is java.lang.NullPointerException

description The server encountered an internal error that prevented it from fulfilling this request.

exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
root cause

java.lang.NullPointerException
	java.util.Calendar.setTime(Calendar.java:1076)
	br.com.caelum.tarefas.dao.JdbcTarefaDao.lista(JdbcTarefaDao.java:56)
	br.com.caelum.tarefas.controller.TarefasController.lista(TarefasController.java:38)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	java.lang.reflect.Method.invoke(Method.java:597)
	org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:685)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:919)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:851)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.53 logs.

Apache Tomcat/7.0.53

Seguem também meus arquivos para implementação desta funcionalidade.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		 xmlns="http://java.sun.com/xml/ns/javaee" 
		 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
		 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
		 id="WebApp_ID" version="2.5">
  <display-name>fj21-tarefas</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>/WEB-INF/spring-context.xml</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>  
  
</web-app>

TarefasController.java

package br.com.caelum.tarefas.controller;

import java.util.List;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;

import br.com.caelum.tarefas.dao.JdbcTarefaDao;
import br.com.caelum.tarefas.modelo.Tarefa;

@Controller
public class TarefasController 
{
	@RequestMapping("novaTarefa")
	public String form(){
		return "tarefa/formulario";
	}
	
	@RequestMapping("adicionaTarefa")
	public String adiciona(@Valid Tarefa tarefa, BindingResult result)
	{
		
		if(result.hasFieldErrors("descricao")){
			return "tarefa/formulario";
		}
		
		JdbcTarefaDao dao = new JdbcTarefaDao();
		dao.adiciona(tarefa);			
		return "tarefa/adicionada";
	}
	
	@RequestMapping("listaTarefas")
	public String lista(Model model)
	{
		JdbcTarefaDao dao = new JdbcTarefaDao();
		model.addAttribute("tarefas", dao.lista());
		return "tarefa/lista";
	}
	
}

JdbcTarefaDao.java

package br.com.caelum.tarefas.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import br.com.caelum.tarefas.jdbc.ConnectionFactory;
import br.com.caelum.tarefas.modelo.Tarefa;

public class JdbcTarefaDao {
	private Connection connection; 

	public JdbcTarefaDao()
	{
		this.connection = new ConnectionFactory().getConnection();
	}

	public void adiciona(Tarefa tarefa)
	{
		String sql = "Insert into tarefas (descricao, finalizado) values(?, ?)";
	
		try
		{
			PreparedStatement stmt =  connection.prepareStatement(sql);
		
			stmt.setString(1, tarefa.getDescricao());
			stmt.setBoolean(2, tarefa.isFinalizado());
		//	stmt.setDate(3, new Date(tarefa.getDataFinalizacao().getTimeInMillis()) );

			stmt.execute();
			stmt.close();
		}catch(SQLException e)
		{
			throw new RuntimeException(e);
		}
	}
	
	public List<Tarefa> lista()
	{
		try{
			List<Tarefa> tarefas = new ArrayList<Tarefa>();
			PreparedStatement stmt = this.connection.prepareStatement("select * from tarefas");
			ResultSet rs = stmt.executeQuery();
			
			while (rs.next())
			{
				Tarefa tarefa = new Tarefa();
				tarefa.setId(rs.getLong("id"));
				tarefa.setDescricao(rs.getString("descricao"));
				tarefa.setFinalizado(rs.getBoolean("finalizado"));
				
				Calendar data = Calendar.getInstance();
				data.setTime(rs.getDate("dataFinalizacao"));
				tarefa.setDataFinalizacao(data);
				
				tarefas.add(tarefa);
			}
			rs.close();
			stmt.close();
			
			return tarefas;
		}
		catch (SQLException e)
		{
			throw new RuntimeException(e);
		}
	}

}

lista.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<a href="novaTarefa">Criar nova tarefa</a>
	<br /><br />
	<table>
		<tr>
			<th>Id</th>
			<th>Descrição</th>
			<th>Finalizado?</th>
			<th>Data de finalização</th>
		</tr>
		<c:forEach items="${tarefas}" var="tarefa">
			<tr>
				<td>${tarefa.id}</td>
				<td>${tarefa.descricao}</td>
				<c:if test="${tarefa.finalizado eq false}">
					<td>Não finalizado</td>
				</c:if>
				<c:if test="${tarefa.finalizado eq true}">
					<td>Finalizado</td>
				</c:if>	
				<td>
					<fmt:formatDate
						value="${tarefa.dataFinalizacao.time}"
						pattern="dd/MM/yyyy"/>
				</td>
			</tr>	
		</c:forEach>	
	</table>
</body>
</html>

spring-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"    
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
	   xmlns:context="http://www.springframework.org/schema/context"    
	   xmlns:mvc="http://www.springframework.org/schema/mvc"    
	   xsi:schemaLocation="http://www.springframework.org/schema/mvc    
	    				   http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd    
	    				   http://www.springframework.org/schema/beans    
	    				   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
	    				   http://www.springframework.org/schema/context    
	    				   http://www.springframework.org/schema/context/spring-context-3.1.xsd">    
	  
		<context:component-scan base-package="br.com.caelum.tarefas.controller" />   
		<mvc:annotation-driven /> 
		<mvc:resources mapping="/resources/**" location="/resources/" />  
		  
		<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">   
			<property name="prefix" value="/WEB-INF/views/"/>   
			<property name="suffix" value=".jsp"/>   
		</bean> 
		
		<bean id="messageSource" class= "org.springframework.context.support.ReloadableResourceBundleMessageSource">
			<property name="basename" value="/WEB-INF/mensagens"></property>
		</bean>  
  
</beans>  

Tarefa.java

package br.com.caelum.tarefas.modelo;

import java.util.Calendar;

import javax.validation.constraints.Size;

import com.sun.istack.internal.NotNull;

public class Tarefa 
{
	private Long id;
	
	@NotNull//(message="{tarefa.descricao.vazia}") 
	@Size(min=5, message="{tarefa.descricao.pequena}")
	private String descricao;
	private boolean finalizado;
	private Calendar dataFinalizacao;
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getDescricao() {
		return descricao;
	}
	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}
	public boolean isFinalizado() {
		return finalizado;
	}
	public void setFinalizado(boolean finalizado) {
		this.finalizado = finalizado;
	}
	public Calendar getDataFinalizacao() {
		return dataFinalizacao;
	}
	public void setDataFinalizacao(Calendar dataFinalizacao) {
		this.dataFinalizacao = dataFinalizacao;
	}
	
}

Agradeço a ajuda de todos.
Abraço,
Fernando Nascimento

Alguém sabe o que pode ser isso?

Alguém?

Veja se no banco a coluna dataFinalização está preenchida em todas as linhas.

Erick,

Este campo não está preenchido em nenhuma linha.
Ele só será populado a partir do momento que as tarefas forem finalizadas.
Não há como listar as tarefas com estes campos null?

Não, pois na sua consulta, como na coluna está null, você está passando null para o objeto:

data.setTime(rs.getDate("dataFinalizacao")); // 1- vem null do banco tarefa.setDataFinalizacao(data); // 2- passa o null para a tarefa

E depois você tenta formatar um campo nulo:

Só se você fizer o tratamento do valor quando estiver nulo no banco.