[Dúvida]Struts 2 - NullPointerException

Olá galera!
Inventei de começar a brincar com o struts 2 (primeira framework que utilizo para desenvolver).

Primeiramente vou mostrar o meu código e gostaria de que me auxiliassem na resolução do erro (e gostaria de umas dicas também para melhorar meu código, pois não comecei há muito tempo…)

Primeiramente a classe abstrata User (modelo)

package br.com.search4help.model;


abstract public class User{

	private String matricula;
	private String cpf;
	private String nome;
	private String email;
	private String login;
	private String password;
	private String checkPassword;
	
	public String getCheckPassword() {
		return checkPassword;
	}

	public void setCheckPassword(String checkPassword) {
		this.checkPassword = checkPassword;
	}

	private boolean chamadoAberto;
	private boolean admin;
	

	public boolean isAdmin() {
		return admin;
	}

	public void setAdmin(boolean admin) {
		this.admin = admin;
	}

	public User(){
		this.chamadoAberto = false;
	}
	
	public String getMatricula() {
		return matricula;
	}

	public void setMatricula(String matricula) {
		this.matricula = matricula;
	}

	public String getCpf() {
		return cpf;
	}

	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getLogin() {
		return login;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	public boolean isChamadoAberto() {
		return chamadoAberto;
	}

	public void setChamadoAberto(boolean chamadoAberto) {
		this.chamadoAberto = chamadoAberto;
	}

}

É abstrata pois existem mais de um tipo de usuário.

Em seguida a classe Funcionario (extends User)

package br.com.search4help.model;


public class Funcionario extends User{ //obtém atributos e  comportamentos de um User; falta implementar outras funcionalidades específicas.

	public Funcionario(){
		this.setAdmin(false);
	}
}

A classe ConexaoInterceptor (Interceptador que abre e fecha conexões com o banco) [é correto utilizar um interceptador para este fim? Pensei em um filtro, porém não descobri como fazer a minha action pegar os dados da conexão através da sessão… :(]

package br.com.search4help.interceptor;

import java.sql.Connection;

import br.com.search4help.jdbc.ConnectionFactory;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class ConexaoInterception implements Interceptor{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private Connection connection;
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void init() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		this.connection = ConnectionFactory.getConnection();
		invocation.getInvocationContext().getSession().put("conexao",connection);
		System.out.println("conexão aberta por interceptor");
		invocation.invoke();
		this.connection.close();
		System.out.println("conexão fechada por interceptor");
		return "ok";
	}

}

A classe funcionarioDAO (que está parcialmente implementada… mas o que importa mesmo é a sua superclasse Logable, onde está o método que me dá dor de cabeça…)

package br.com.search4help.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import br.com.search4help.model.Funcionario;

public class FuncionarioDAO extends Logable{
	
	public FuncionarioDAO(Connection connection){
		super(connection);
	}
	
	public boolean abrirChamado(Funcionario func) throws SQLException{
		try{
			PreparedStatement stmt = this.connection.prepareStatement("");
			return true;
		}catch(SQLException e){
			e.printStackTrace();
			return false;
		}
	}
}

A index.jsp, onde o usuário fornece o login e a senha para entrar

<%@ 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">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<div>
	<form name="formulario" action="loginFuncionario" method="POST">
		<table border="1" cellpadding="5" cellspacing="0">
			<thead>
				<tr>
					<th colspan="2" scope="col">Search4help :: Login</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td align="right">Login: </td><td><input type="text" name="funcionario.login" size="20" /></td>
				</tr>
				<tr>
					<td>Password:</td><td><input type="password" name="funcionario.password" size="20" /></td>
				</tr>
				<tr>
					<td colspan="2" align="right"><input type="submit" name="submit" value="Entrar" /></td>
				</tr>
			</tbody>
		</table>	
		</form>
	</div>
</body>
</html>

Segue abaixo a minha Action, a classe LoginAction, que tenta utilizar o método “isAccessGranted” para jogar na sessão um usuário válido através do login e senha fornecidos pelo usuário da index.jsp.

package br.com.search4help.action;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

import br.com.search4help.dao.AdminDAO;
import br.com.search4help.model.Funcionario;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

@ParentPackage("default")
public class LoginAction extends ActionSupport{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Funcionario funcionario;
	private Connection connection;

	@Action(value = "loginFuncionario", results = {
			@Result(name = "ok", location = "/admin/index.jsp"),
			@Result(name = "invalido", location = "/index.jsp") }, interceptorRefs = {
				@InterceptorRef("conexao")
			}

	)
	public String execute() throws SQLException {
		try {
			this.connection = (Connection) ActionContext.getContext().getSession().get("conexao"); //<--pega a conexão gerada pelo interceptor que está na sessão.
			if ((new AdminDAO(connection).isAccessGranted(funcionario))) {//<-- AQUI É A LINHA QUE DISPARA O NullPointerException!!                                    
				ActionContext.getContext().getSession().put("usuarioLogado",funcionario);//<-- caso OK, joga o usuário na sessão.
					System.out.println("funcionario registrado na sessão");
			} else {
				System.out.println("erro durante abertura de sessão");
				return "invalido";
			}
			return "ok";
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
	}

	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

	public Funcionario getFuncionario() {
		return funcionario;
	}

	public void setFuncionario(Funcionario funcionario) {
		this.funcionario = funcionario;
	}
}

E finalmente a classe Logable que dispara o NullPointerException: a classe abstrata Logable (trata-se de uma DAO… acredito que poderia fazer dela uma interface, porém a implementação deve ser exatamente a mesma para todos os que herdarem o método “isAccessGranted”, o que acham?)

package br.com.search4help.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import br.com.search4help.model.Funcionario;
import br.com.search4help.model.User;

abstract public class Logable {
	
	public Connection connection;
	
	public Logable(Connection connection){ //<-- Construtor com injeção de dependencia... depende de um Connection
		this.connection = connection;
	}
	public boolean isAccessGranted(User user) throws SQLException{
		try{
			String sql = "SELECT * FROM tbl_funcionario WHERE login=? and password=?"; // <-- verifica a existência de um usuário pelo login e password.
			PreparedStatement stmt = connection.prepareStatement(sql);
			stmt.setString(1,user.getLogin());
			stmt.setString(2,user.getPassword());
			ResultSet rs = stmt.executeQuery();
			while(rs.next()){
                            return true;
			}
			return false;
		}catch(SQLException e){
			e.printStackTrace();
			return false;
		}catch(NullPointerException e){
			System.out.println("O usuário é nulo");
			e.printStackTrace();
			return false;
		}
	}
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>search4help</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>
	
	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<servlet>
		<servlet-name>login</servlet-name>
		<servlet-class>br.com.search4help.controller.ServletLoginFuncionario</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>login</servlet-name>
		<url-pattern>/login.do</url-pattern>
	</servlet-mapping>
	
	<servlet>
		<servlet-name>logout</servlet-name>
		<servlet-class>br.com.search4help.controller.ServletLogout</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>logout</servlet-name>
		<url-pattern>/logout.do</url-pattern>
	</servlet-mapping>
	
	<servlet>
		<servlet-name>function</servlet-name>
		<servlet-class>br.com.search4help.controller.ServletFunction</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>function</servlet-name>
		<url-pattern>/admin/function.do</url-pattern>
	</servlet-mapping>
	
	<filter>
		<filter-name>filtroLogin</filter-name>
		<filter-class>br.com.search4help.controller.FiltroLogin</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>filtroLogin</filter-name>
		<url-pattern>/admin/*</url-pattern>
	</filter-mapping>
	
	<filter>
		<filter-name>filtroConexao</filter-name>
		<filter-class>br.com.search4help.controller.FiltroConexao</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>filtroConexao</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<session-config>
		<session-timeout>3</session-timeout>
	</session-config>
</web-app>

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE struts PUBLIC  
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  
"http://struts.apache.org/dtds/struts-2.0.dtd">  
<struts>  
    <!-- configs iniciais padrão do Struts 2 -->  
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />  
    <constant name="struts.devMode" value="false" />  
  
    <!-- mapeamento de minhas classes e respostas (forwards) -->  
    <package name="default" extends="convention-default">  
        
        <interceptors>
        	<interceptor name="conexao" class="br.com.search4help.interceptor.ConexaoInterception">
        	</interceptor>
        </interceptors>
    </package>  
      
</struts>  

É isso galera…
gostaria de saber o por quê do NullPointerException, uma vez que o struts deveria instanciar e popular o “funcionario” e passar para o método através dos atributos e dos getters e setters da action… (correto???)

agradeço!

Up!