[RESOLVIDO] Struts 2 não popula objetos

Galera… preciso de uma ajuda…
Postei o código inteiro das minhas classes no tópico abaixo…
Queria somente saber se alguem já teve o problema de o struts 2 não popular os objetos…

Tipo, pelo que eu sei, o struts INSTANCIA e POPULA os objetos AUTOMATICAMENTE, utilizando somente os controles da JSP sinalizados pelo nome da seguinte maneira (exemplo)

<form name="forulario" action="funcionarioLogin" method="POST>
 ...
     <intput type="text" size="15" name="funcionario.login" />
     <intput type="password" size="15" name="funcionario.password" />
 ...
</form>

Bom… pelo que eu entendi, ao submeter o formulário (além de ter mapeado 21312321 vezes tudo correto no web.xml e no struts.xml), o struts mágicamente executa a action SOMENTE com uma referência ao objeto do tipo FUNCIONARIO e os POPULA AUTOMATICAMENTE!

//TODOS os imports e TODAS as anotações (inclusive uma de interceptação e talz...)
public class LoginAction extends ActionSupport{
   private Funcionario funcionario; //<-- Como é que essa merda popula o objeto usando só uma REFERENCIA??! 

   public String execute(){
          if((new AdminDAO().isAccessGranted(funcionario))){ //<-- Checa a existencia de um funcionario pelo método e... NULLPOINTEREXCEPTION!!!
                      //Joga o funcionario cadastrado na sessão...
          }
   }
}

Pelo que eu entendi: NullPointerException pq eu passei uma REFERÊNCIA e não um OBJETO porque eu não instanciei o objeto (Funcionario funcionario = new Funcionario()). O que pelo que eu sei é DEVER DO STRUTS 2! e não meu…

Observação:
Não coloquei no código, mas eu crier os getters e setters da referência Funcionario (public void setFuncionario(Funcionario funcionario), public Funcionario getFuncionario())…

E ai… quem vai ser o herói?

Isso é uma das coisas mais simples de fazer no Struts2 e não deveria estar dando problema. Você executou em modo debug para saber com o objeto “funcionário” está chegando no método “execute” de seua Action?

A classe Funcionário possui os getters e setters para “login” e “password”?

Passa também teu “struts.xml” pra darmos uma olhadinha :wink:

Olá, olhando pro seu código aparentemente está tudo ok.
Bom, acredito que o erro esteja em algum outra parte do seu codigo. não sou fã do xml
e do modo de ter que estender ActionSuport. se você puder, pode migrar do xml para anotations já que isso ajudar MUITO no desenvolvimento com struts2
você pode ver como é bem mais facil desenvolver com anotations no struts nesse topico:


struts2 de um grande passo em relação ao 1, e seria interessante se aprendessemos os fit’s dessa tecnologia.
Enfim, eh apenas uma dica :wink:

t+.

Vamos lá:

*no meu pacote model, existe a classe Funcionario devidamente implementada com os seus métodos de acesso getters e setters.

Abaixo vem o código do index.jsp que é nada mais nada menos do que a tela em que o usuário faz o logon no sistema.

<%@ 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>  

Abaixo segue o código completo da Action que está dando o problema de NPE

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

Abaixo segue o 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>  

O resto está configurado e funcionando…
Já debuguei o método “isAccessGranted” passando para ele um Funcionario instanciado e populado… Sem erros…
O problema está quando a action usa o método passando a “referência” (Funcionario funcionario)… BAMM!! NullPointerException!
apareceu tanto erro aqui que eu quase precisei de 2 monitores pra ver tudo… O compilador faltou me xingar…
Alguém já passou por isso? to nessa há mais de uma semana e meus estudos empacaram por conta disso…

olá smallpox,
eu fiz o teste aqui no meu ambiente para testar apenas o funcionario se estava chegando ok para o struts
tirei interceptors, dao, etc. apenas testei o que realmente importava : o funcionario.
E funcionou perfeitamente, o struts conseguiu ler o funcionario e preencheu os seus dados corretamente na requisição.
o código de exemplo está disponivel em: http://ge.tt/2qNHdnL
tente tirar as dependencias da action e verifique apenas o funcionario para verificar se ele esta indo ok mesmo,
ou se em algum momento ele é anulado.
qlquer coisa é so postar

t+

Agradeço a consideração e ao esforço em me ajudar…
Irei fazer esse teste e retorno!
obrigado!

Ora, então o NPE não é relativo ao objeto “funcionario” e sim em alguma coisa no seu DAO.

Amigo, posso dar opiniões sobre seu código?

  1. Evite controlar a conexão com banco pelo espoco de sessão. Isso é um “anti-pattern”. Procure adotar a idéia “Open Session In View” (mesmo que não use Hiberntae/JPA dá pra aplicar seu conceito.

1.1. Ora, se estás pegando uma conexão com banco do scopo de sessão, não entendi o porque dos get e set para o objeto conexão em sua Action.
1.2. Se criou um Interceptor para a conexão, por que não o usou para injetar a conexão na Action ao invés de forçar a Action a buscar da sessão?

  1. Dá pra deixar seu código bem menor com uso de CONVENÇÕES ao invés de anotações. Os códigos no blog recomendado pelo colega são bons mas com convenção dá pra ficarem BEM menores. Assim o uso de anotações só se faz necessário em poucos casos como redirecionamento de ações e “result types” diferentes como “strem”, “jasper”, etc.

Espero ter ajudado.

Pra você ter uma idéia, a Actions apresentada no blogo do colega poderia ficar assim:

[code]package br.com.vonjuliano.struts2simples.action.carro;

// mesmos imports

// como mudei para um sub-pacote “carro”, o namespace “/carro” ficamapeado automaticamente!
// muita gente acha "falta de elegância criar uma Action estedendo outra. Acredite: essa suposta “deselegância” traz 1000000 benefícios! Estenda o ActionSupport e seja bem mais feliz!
public class CarroAction extends ActionSupport {

private final CarroDao dao;

private List<Carro> carros;

private Carro carro;

public CarroAction() {
    this.dao = new JdbcCarroDao();
}

@Action("lista") // O Struts2 mapeia automaticamente para "/WEB-INF/content/carro/lista.jsp"). E o mapeamento é automaticamente "/carro/lista"    
public String lista() {
    carros = dao.lista();
    return SUCCESS;
}

// O Struts2 chama o "/WEB-INF/content/carro/adiciona.jsp" se você invocar "/carro/adiciona!input". Simples assim! Não precisa fazer mapeamentos só para entradas! No formulário do "adiciona.jsp" você configura para chamar o mapeamento "/carro/adicionar". Simples assim!
@Action(value = "adiciona", results = @Result( type = "redirectAction", params = { "actionName", "lista" }))
public String adiciona() {
    dao.adiciona(carro);
    return SUCCESS;
}

public List<Carro> getCarros() {
    return carros;
}

public Carro getCarro() {
    return carro;
}

public void setCarro(Carro carro) {
    this.carro = carro;
}

}[/code]

Ai você pode pensar: “Não quero que meus JSPs fiquem em ‘/WEB-INF/content/’”. Tranquilo. Isso pode ser configurável em apenas 1 linha por todo o projeto.

jyoshiriro, obrigado pela sua resposta.

Em primeiro lugar, gostaria de comunicar-lhes que eu resolvi o problema (não sei ao certo como).

A minha nova classe Logable ficou como a seguinte (que anteriormente retornava um boolean, e agora um objeto Funcionario)

[code]abstract public class Logable {

public Connection connection;

public Logable(Connection connection){
	this.connection = connection;
}
public Funcionario isAccessGranted(User user) throws SQLException{
	try{
		String sql = "SELECT * FROM tbl_funcionario WHERE login=? and password=?";
		PreparedStatement stmt = this.connection.prepareStatement(sql);
		stmt.setString(1,user.getUserLogin());
		stmt.setString(2,user.getUserPassword());
		ResultSet rs = stmt.executeQuery();
		while(rs.next()){
			Funcionario funcionario = new Funcionario();
			funcionario.setMatricula(rs.getString("matricula"));
			funcionario.setCpf(rs.getString("cpf"));
			funcionario.setNome(rs.getString("nome"));
			funcionario.setUserLogin("login");
			funcionario.setUserPassword("password");
			return funcionario;
		}
		return null;
	}catch(SQLException e){
		e.printStackTrace();
		return null;
	}
}

}[/code]

Agora na primeira ocorrência de um funcionário que seja válido, um novo objeto do tipo Funcionario é criado e populado dentro do método isAccessGranted da minha DAO Logable…

O objeto é retornado para a variável de referência (Funcionario) que chamou o método Logable.isAccessGranted a partir da minha Action LoginAction…

Segue abaixo a minha LoginAction

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.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

import br.com.search4help.dao.AdminDAO;
import br.com.search4help.jdbc.ConnectionFactory;
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;
	
	public Funcionario getFuncionario() {
		return funcionario;
	}

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

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	private Connection connection;

	@Action(value = "loginFuncionario", results = {
			@Result(name = "ok", location = "/admin/index.jsp"),
			@Result(name = "invalido", location = "/index.jsp") })
	public String execute() throws SQLException {
		try {
			this.connection = ConnectionFactory.getConnection();
			ActionContext.getContext().getSession().put("conexao", connection);
			this.connection = (Connection) ActionContext.getContext()
					.getSession().get("conexao");
			funcionario = new AdminDAO(connection).isAccessGranted(funcionario);
			if (this.funcionario != null) {
				ActionContext.getContext().getSession().put("usuarioLogado",
						funcionario);
				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 "invalido";
		}
	}

}

Agora funciona…

Em segundo lugar, gostaria de comentar que pra mim a suas observações e dicas (jyoshiriro) são de grande valor… Estou começando a programar com struts 2 agora e é a primeira vez que tenho contato com uma framework… Gostaria de que sempre que meu código estiver fora dos design patterns, me avisem! É bom para criar o hábito de programar já no padrão… Gostaria até de uma referência de design patters para java J2EE… algum tutorial ou livro… existe?

Em terceiro, jyoshiriro

pode me exemplificar? Seria de muita utilidade para mim…