Classe de Interface no VRaptor 3

Oi pessoal!
Fiz uma aleração na aplicação, eu tinha uma classe @Entity Usuario, agora eu tenho uma classe de interface Usuario e duas classes que serão persistidas, a Cliente e outra Funcionario.

Classe de interface Usuario:

public interface Usuario  {
	public String getNome();
	public String getEmail();
	public String getLogin();
	public String getSenha();
	public String getRole();	

}

Classe Funcionario:

@Entity
public class Funcionario implements Usuario, Serializable {

	private static final long serialVersionUID = 1L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;	
	private String nome;
	private String email;
	private String login;
	private String senha;
	private String role;	

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

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

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

	public void setSenha(String senha) {
		this.senha = senha;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getEmail() {
		return email;
	}

	public String getLogin() {
		return this.login;
	}

	public String getRole() {		
		return this.role;
	}

	public String getSenha() {
	    return this.senha;
	}

	public String getNome() {
		return this.nome;
	}

}

Ao chamar a aplicação no Browser, gerou as tabelas corretamente, criei um funcionario no banco com login e senha e ao digitar isto na aplicação me deu o erro:

exception

java.lang.IllegalArgumentException: Vraptor does not support this interface or abstract type: br.com.imobiliaria.bean.Usuario
	br.com.caelum.vraptor.http.ognl.GenericNullHandler.instantiate(GenericNullHandler.java:65)
	br.com.caelum.vraptor.http.ognl.ReflectionBasedNullHandler.nullPropertyValue(ReflectionBasedNullHandler.java:79)
	ognl.ASTProperty.getValueBody(ASTProperty.java:118)
	ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
	ognl.SimpleNode.getValue(SimpleNode.java:236)
	ognl.ASTChain.setValueBody(ASTChain.java:222)
	ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220)
	ognl.SimpleNode.setValue(SimpleNode.java:279)
	ognl.Ognl.setValue(Ognl.java:737)
	ognl.Ognl.setValue(Ognl.java:783)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createViaOgnl(OgnlParametersProvider.java:132)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.createRoot(OgnlParametersProvider.java:108)
	br.com.caelum.vraptor.http.ognl.OgnlParametersProvider.getParametersFor(OgnlParametersProvider.java:90)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.getParametersFor(ParametersInstantiatorInterceptor.java:83)
	br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:68)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:42)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.util.jpa.JPATransactionInterceptor.intercept(JPATransactionInterceptor.java:46)
	br.com.caelum.vraptor.core.InstantiatedInterceptorHandler.execute(InstantiatedInterceptorHandler.java:47)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.interceptor.InterceptorListPriorToExecutionExtractor.intercept(InterceptorListPriorToExecutionExtractor.java:46)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:80)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:67)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

VRaptor não suporta Interface? Como poderia implementar essa tática de Interface para Cliente e Funcionário?

Abraço!!! =)

Acho que o problema não é o que o vraptor não trabalha com interfaces :). Provavelmente vc tem um metodo login(Usuario usuario), o lance aí é que o vraptor vai tentar instanciar esse usuario para popular com os valores vindos do formulario, e, como isso é uma interface, não vai rolar. Seu parametro deveria ser de um tipo concreto.

Alberto

Pois é alots_ssa, ele está tentando instanciar o Usuario que agora é uma Interface, no AccesControllerInterceptor o restrictionResult pega o this.usuarioLogado.getPerfil(), este por sua vez instancia o Usuario, que possui o método getPerfil(), então precisaria que o usuarioLogado instanciasse o Cliente e o Funcionario, pegando seus respectivos getPerfil(), ou seja, tomar conta de duas classes ao invés de uma.

private Usuario usuarioLogado;

Já que Usuario é uma interface, como poderia instanciar o Cliente e o Funcionario através da classe usuarioLogado?

Abraço!

se vc tem uma interface Usuario com getLogin() e getEmail(), e vier na requisição os parâmetros:

usuario.login=guevara
usuario.email=guevara@email.com

Como o vraptor consegue saber qual implementação usar? Não dá pra saber! Como vc faz pra diferenciar se é um Cliente ou um Funcionário na requisição?

de qqer forma, vc pode trocar essa sua interface por composição:

@Entity
public class Cliente {
   //...
   private Usuario usuario;
}

@Entity
public class Funcionario {
   //...
   private Usuario usuario;
}

e esse usuário tem aquelas propriedades que estão na interface… Se vc quiser que os dados fiquem nas tabelas Funcionario e Cliente, é só anotar o atributo usuario com @Embedded

Oi Lucas!!
Pelo que eu entendi, a classe Usuario ficaria assim:

@Embedded
public class Usuario  {
	public String getNome();
	public String getEmail();
	public String getLogin();
	public String getSenha();
	public String getRole();
}

E como eu anoto esse relacionamento dentro da classe Funcionario e Cliente?

@Entity
public class Funcionario {

  //...Preciso de anotação aqui?
   private Usuario usuario;
}

Abraço!!

Se seu funcionário é um usuário, assim como o cliente, você faz isso:

Só que pela lógica Usuario deveria ser abstrata, porém nesse caso o vraptor também não irá conseguir instanciar :smiley:

Pois é Garcia, a porta de entrada para a aplicação é o UsuarioLogado instanciar o Usuario, e este não pode ser interface e nem classe abstrata, que sinuca heim? =)

A dica do Lucas que eu não entendi ainda, em relação a classe Usuario dentro das classes Funcionario e Cliente.

Abraço!!

[quote=Guevara]Pois é Garcia, a porta de entrada para a aplicação é o UsuarioLogado instanciar o Usuario, e este não pode ser interface e nem classe abstrata, que sinuca heim? =)

A dica do Lucas que eu não entendi ainda, em relação a classe Usuario dentro das classes Funcionario e Cliente.

Abraço!![/quote]

Quando ao fato de Usuario ser abstract ou interface isso é fato. O mais correto é class abstrata para usar a herança do hibernate. Porém acho que você que está fazendo errado. Porque você quer passar a classe abstrata e não a implementação? Você usa a mesma tela para cliente e a mesma para funcionário? Penso que embora possam todos herdar de usuário, na verdade são telas com cadastros e talvez até regras diferentes, então deveriam estar em telas diferentes.

A idéia é essa mesma, cadastro em telas diferentes, inclusive teria que fazer clienteLogado e funcionarioLogado e no AccesControllerInterceptor ficaria:

private ClienteLogado clienteLogado;
private FuncionarioLogado funcionaLogado;	
	
	public AccesControllerInterceptor(ClienteLogado clienteLogado, FuncionarioLogado funcionarioLogado, RestrictionChecker restrictionChecker, Result result) {
		this.result = result;
		this.restrictionChecker = restrictionChecker;
		this.clienteLogado = clienteLogado;
                this.funcionarioLogado = funcionarioLogado;
	}

restrictionResult = this.restrictionChecker.checkRestrictions(resourceMethod.getMethod(), this.clienteLogado.getPerfil());
restrictionResult = this.restrictionChecker.checkRestrictions(resourceMethod.getMethod(), this.funcionarioLogado.getPerfil());

Será que o restrictionResult suporta pegar esses dois ai?

não gente, sem herança… o que eu sugeri era fazer uma composição mesmo…

vc poderia fazer a classe Usuario ser uma @Entity e anotar os fields com @OneToOne para o relacionamento com Cliente e Funcionario…

daí sua lógica de login só usaria o Usuário

Lucas, mas não sei o que é pior… se é um cliente e funcionário ser um usuário ou um cliente e funcionário ter um usuário. :stuck_out_tongue:

Mas de qualquer forma, tua idéia faz um pouco mais de sentido nesse confuso contexto.

Usuário não no sentido de cara que usa o sistema, mas no sentido de credenciais para entrar no sistema…

afinal vc sempre fala: qual o seu usuário no twitter? qual o seu usuário do Orkut? etc…

Guys,

Cara, não vejo erro em nenhuma das situações. Em um sistema, um funcionário pode sim ser um usuário, bem como um cliente também pode sê-lo.

O problema é que Usuario acabaria sendo abstrato demais nessa situação, e ficaria impossível saber qual tipo de usuário (funcionário ou cliente) o framework deve injetar.

Então, deve-se usar as classes especializadas mesmo. Só que, para evitar a tão odiada (e mal usada) herança, o ideal é usar composição (um objeto é composto de outro).

Sei que isso essa discussão é velha, mas só quis dizer que não enxergo o problemas nas abordagens propostas. :thumbup:

eu discordo da estrategia de que um Funcionário pode ser um usuário…

Na verdade nem Cliente nem Funcionário, devem vim de uma Herença… eles são na verdade Pessoas…

Uma Pessoa não é um funcionario, ou uma pessoa é um cliente… Uma pessoa é uma pessoa e pronto…

Porem, quando uma Pessoa arruma um emprego, ela passa a ter informações relativa a este emprego, e portanto passam a ter um Funcionário, ou seja, a ter informações de funcionario… o mesmo com cliente, ate pq, um Funcionário também pode ser um cliente…

Esses casos são resolvidos fácilmente com composição.

Pessoa -- has --> FuncionarioInfo |- -- has --> ClienteInfo |- -- has --> UsuarioInfo

assim vc evita problemas com herença, e não vai se deparar com becos sem saída como o caso de um cadastro duplicado, só pq um Funcionário resolveu comprar algo na sua própria empresa … O.o …

Enfim, prefira composição a Herença…

Espero ter ajudado…

Rapaz, eu desvirtuei todo o foco do tópico, sorry guys :oops: Guevara, não repara, a intenção é apenas te ajudar. Mas desvirtuando um pouco mais, concordo com tudo que o Laveri disse.

Penso que o ideal é fazer como o que o Lucas disse, criar três classe distintas (Usuario, Cliente e Funcionario), e fazer as telas distintas para cliente e funcionário. Caso um cliente ou um funcionario tenham acesso ao sistema através de login, aí você pode, por composição, incluir as informações de login/senha.

O que você acha, Guevara? Creio que assim você nem mesmo terá aqueles problemas iniciais do tópico.

Oi pessoal! =)
Obrigado pelas dicas, eu estava com a intenção de evitar herança, até pelo que eu já li a respeito, preferir composição à herança, mas não estou conseguindo colocar em código isso, os exemplos que eu pesquisei não batem com o que eu tenho para fazer essa composição.

Os atributos da classe Usuario serão os mesmos nas classes Cliente e Funcionario?
Fiz a classse Usuario:

@Entity
public class Usuario implements Serializable {
	
	private static final long serialVersionUID = 1L;	
	
	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name="id_cliente")
	private Cliente cliente;
	
	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name="id_funcionario")
	private Funcionario funcionario;
    
        @Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
        private String login;
	private String senha;
	private String nome;
	private String role;
        //getters and setters

E a classe Funcionario:

@Entity
public class Funcionario implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	@Embedded
	@OneToOne(mappedBy = "usuario")
	private Usuario usuario;
	
	public Usuario getUsuario() {
		return usuario;
	}

	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}
        
       @Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="id_funcionario")
	private Long idFuncionario;	
	private String login;
	private String senha;
	private String nome;
	private String role;
        //getters and setters

Isso que eu fiz não funciona. =(
Os atributos login, nome, senha e role ficam na classe Usuario ou nas classes Cliente e Funcionario? Não sei em que lado do relacionamento ficam os atributos, isso não ficou claro pra mim.
Outro problema, o método getPerfil() têm que estar na classe Usuario, senão o usuarioLogado não consegue obter o perfil.
Abraço!!

mova tudo que é relativo ao login do usuário pra classe Usuario

Guevara… se quiser colocar a ligação com Usuario em mais de uma classe, então cria uma INterface “HasUsuario” nesta interface só terá um método “getUsuario()” (no máximo 2, com o setUsuario, mas só coloque o segundo quando precisar em algum método)…

algo como:

public class Funcionario implements HasUsuario {... } public class Cliente implements HasUsuario {... } public class Etcs implements HasUsuario {... }

Fora isso onde vc precisar de um usuário vc coloca

public void seuMetodo(HasUsuario hasUsuario) { seuMetodo(hasUsuario == null ? null : hasUsuario.getUsuario()); } public void seuMetodo(Usuario usuario) { //... aki vc coloca sua lógica }

pronto agora vc consegue usar polimorfismo…

Ps.: ahh claro, faça o que o lucas falou, mover as informações para dentro de usuário