[RESOLVIDO] Como injetar e obter valores de variáveis na SESSÃO do Usuário com o V|Raptor 3.2.0?

14 respostas
softwork

Pessoal.
Estou com uma dúvida PRIMÁRIA com esta nova versão V|Raptor 3.2.0

Na versão 2.6.0 para colocar e receber valores na sessão eu fazia assim:
...
...
...
    @In(scope=ScopeType.SESSION, required = false)
    @Out(scope=ScopeType.SESSION)
    private Integer resultSize = 0;
...
...
...
Desta forma em uma determinada lógica eu consigo atribuir valor para a sessão, exemplo:
private String adiciona() {
        this.resultSize = 0;
...
...
...
    }
Já em outra lógica eu consigo obter/enviar o valor da váriável que esta na sessão, exemplo:
public void lista() {
        Integer pageNumber = Util.getPageDisplayTag(this.request);
        if (this.resultSize <= 0) {
...
...
...
            this.resultSize = this.dao.getUsuarioDAO().recordCount();
        }
    }

Com esta nova versão eu sei que tenho que utilizar o Result para enviar variáveis e seus valores, porém não sei como colocá-lo no escopo de sessão e como obtê-lo da sessão.

Alguém poderia me orientar, por favor ?

Exemplo de código que tenho atualmente:
@Path("/usuario/lista")
    public void lista() {
        Integer pageNumber = Util.getPageDisplayTag(this.request);

        if (this.resultSize <= 0) // Aqui preciso obter o valor que esta na SESSÃO do Usuário.
            this.resultSize = this.dao.getUsuarioDAO().recordCount();
        
        Collection<Usuario> usuarios = this.dao.getUsuarioDAO().listAll(pageNumber, 30);

        this.result.include("resultSize", this.resultSize); // Preciso injetar o valor na SESSÃO do Usuário.
        this.result.include("usuarios", usuarios);
    }
Obrigado.

14 Respostas

Lucas_Cavalcanti

o jeito mais educado no VRaptor seria criar uma classe Session scoped:

@SessionScoped
@Component
public class Paginator {
     private int resultSize;
     private int page;
     //....
}

receber essa classe no construtor do controller (ou de alguma outra classe que vc precisar disso) e interagir com ela.

vc pode colocar métodos mais representativos e úteis do que só os getter/setters

Lucas_Cavalcanti

como vc tá usando DisplayTag, tem esse interceptor que pode te ajudar:

D
@Component
@SessionScoped
public class SessionUser implements Serializable {

   private User user;
//get/set

}
dai no seu controller:
@Resource
public class MeuController{
	
	private final SessionUser sessionUser;
	public MeuController(SessionUser sessionUser){
		this.sessionUser = sessionUser;
	}
	
	public void form(){
		System.out.println(sessionUser.getUser().getName());
	}
}
D

Nossa, cheguei tarde, :lol:

softwork

Lucas Cavalcanti:
o jeito mais educado no VRaptor seria criar uma classe Session scoped:

@SessionScoped
@Component
public class Paginator {
     private int resultSize;
     private int page;
     //....
}

receber essa classe no construtor do controller (ou de alguma outra classe que vc precisar disso) e interagir com ela.

vc pode colocar métodos mais representativos e úteis do que só os getter/setters

Valeu Lucas, eu fiz a implementação da tua dica!
Veja como ficou:

...
...
@SessionScoped
@Component
public class PaginatorDisplayTag {
    
    private int resultSize = 0;
    private int pageSize = 30;
    
    public int getResultSize() {
        return resultSize;
    }
    
    public void setResultSize(int resultSize) {
        this.resultSize = resultSize;
    }
    
    public int getPageSize() {
        return pageSize;
    }
    
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    
}

Em meu Controller:

@Resource
public class UsuarioController {
    
    private final DAOFactory          dao;
    private final Result              result;
    private final HttpServletRequest  request;
    private final PaginatorDisplayTag pagina;
    
    public UsuarioController(DAOFactory daoFactory, Result result, HttpServletRequest request, PaginatorDisplayTag pagina) {
        this.dao = daoFactory;
        this.result = result;
        this.request = request;
        
        this.pagina = pagina;
        this.result.include("paginator", this.pagina);
    }

    @Path("/usuario/lista")
    public void lista() {
        Integer pageNumber = Util.getPageDisplayTag(this.request);
        if (this.pagina.getResultSize() <= 0)
            this.pagina.setResultSize(this.dao.getUsuarioDAO().recordCount());
        
        // Collection<Usuario> usuarios = this.dao.getUsuarioDAO().selectByNamedQuery("selectUsuarios");
        Collection<Usuario> usuarios = this.dao.getUsuarioDAO().listAll(pageNumber, this.pagina.getPageSize());
        this.result.include("paginator", this.pagina);
        this.result.include("usuarios", usuarios);
    }

    @Post
    @Path("/usuario/salvar")
    public void salvar(Usuario usuario) {
        if (usuario != null) {
            if (usuario.getId() == null)
                this.dao.getUsuarioDAO().insert(usuario);
            else
                this.dao.getUsuarioDAO().update(usuario);
        }
        this.lista();
        this.pagina.setResultSize(0);
        this.result.include("paginator", this.pagina);
        this.result.redirectTo(this.getClass()).lista();
    }

Porém agora estou tendo problema no meu lista.jsp, veja o códgo:

<display:table id="usuario" name="${usuarios}" sort="list" 
                   pagesize="${paginator.pageSize}" partialList="true" size="${paginator.resultSize}"
                   requestURI="<c:url value="/usuario/lista"/>" class="media">
                   <display:column property="id" titleKey="usuario.id" sortable="true" class="coluna_icones"/>
                   <display:column property="nomeDoUsuario" titleKey="usuario.nome" sortable="true"/>
                   <display:column titleKey="coluna.opcoes" class="coluna_opcoes6 " headerClass="coluna_opcoes6">
                        &nbsp;
                        <a href="<c:url value="/usuario/editar/${usuario.id}"/>">Editar</a> 
                        &nbsp;
                        <a href="<c:url value="/usuario/excluir/${usuario.id}"/>">Excluir</a>
                        &nbsp;
                   </display:column>
    </display:table>

Note que no parâmetro requestURI eu passo a url da logica “lista”:

requestURI="<c:url value="/usuario/lista"/>"

Mas o seguinte erro me retorna:

GRAVE: Servlet.service() for servlet default threw exception
br.com.caelum.vraptor.view.ResultException: org.apache.jasper.JasperException: /WEB-INF/jsp/usuario/lista.jsp(7,45) Unterminated &lt;display:table tag
	at br.com.caelum.vraptor.view.DefaultPageResult.forward(DefaultPageResult.java:69)
...
...
...
Caused by: org.apache.jasper.JasperException: /WEB-INF/jsp/usuario/lista.jsp(7,45) Unterminated &lt;display:table tag
	at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:40)
...
...
...

Porém este erro é por causa da chamada <c:url value="/usuario/lista"/>
Então se eu modifico para:

requestURI="/csr/usuario/lista"

O erro não é disparado, no entanto o construtor do Controller é sempre chamado e os valores voltam para o “default”.

Estou fazendo algo de errado ?

Lucas_Cavalcanti

não dá pra usar <c:url> dentro dos attributos do displayTag…

vc tem que fazer algo assim, que talvez funcione:

<c:url value="xxx" var="abc"/>

...
requestURI="${abc}"

tá certo o construtor do Controller sempre ser chamado, mas a instância do Paginator é a mesma dentro da Sessão do usuario.

se vc muda os parametros do paginator, mesmo assim ele volta pro valor inicial?

softwork

Lucas Cavalcanti:
não dá pra usar <c:url> dentro dos attributos do displayTag…

vc tem que fazer algo assim, que talvez funcione:

<c:url value="xxx" var="abc"/>

...
requestURI="${abc}"

tá certo o construtor do Controller sempre ser chamado, mas a instância do Paginator é a mesma dentro da Sessão do usuario.

se vc muda os parametros do paginator, mesmo assim ele volta pro valor inicial?

Oi Lucas!

Eu consegui acertar o valor correto para requestURI, veja:

Desta forma não recebo a mensagem de erro e os valores permanecem inalterados em sessão.

Já em relação ao DisplayTagInterceptor, que você me indicou, eu teria que colocar a anotação e receber os parâmetros na lógica “lista()” e como na minha lógica salva() eu faço um redirecionamento para lista(), terei que também anotar a lógica salva() para receber os parâmetros, correto ?

PS: Acho que não vai funcionar, pois salva() é chamado de outro JSP e nele não há o displayTag.

Lucas_Cavalcanti

quando vc fizer o redirect, vc pode passar os valores padrão

softwork

Lucas, vou implementar esta dica e depois coloco aqui o resultado.

Mas agora lhe faço outra pergunta.

E se eu tivesse apenas uma variáveis para ser colocado em SESSÃO, eu teria que criar uma classe e anotá-la com (@SessionScoped ou @ApplicationScoped e etc…) ?

Existe uma forma “Malcriada” de implementação para que o V|Raptor possa entender ?

Obrigado.

Lucas_Cavalcanti

vc está achando trabalhoso desenvolver em geral ou só essa parte da Session?

tem uma issue no VRaptor para implementar isso, mas a gente ainda não achou uma forma boa de fazer https://github.com/caelum/vraptor/issues/#issue/188

Não há nada de errado criar uma classe que encapsula um único valor, isso é até uma boa prática. É raro ter um int ou uma string perdida no sistema que não tenha um significado maior que possa ser colocada numa classe. É bem mais fácil dar manutenção depois se for uma classe do que se for um int espalhado pelo código, não é verdade?

E não é uma simples classe que só encapsula os valores, ela pode ter comportamento também. O seu paginator por exemplo poderia ser usado para cuidar dentro da paginação dos seus daos, com um método do tipo:

public class Paginator {

   public Query pagina(Query query) {
         query.setFirstResult(page*PAGE_SIZE);
                .setMaxResults(PAGE_SIZE);
         return query;
   }
}

Ou seja, não é um trabalho a mais a toa, ele tem algumas vantagens.

softwork

Lucas Cavalcanti:
vc está achando trabalhoso desenvolver em geral ou só essa parte da Session?

tem uma issue no VRaptor para implementar isso, mas a gente ainda não achou uma forma boa de fazer https://github.com/caelum/vraptor/issues/#issue/188

Não há nada de errado criar uma classe que encapsula um único valor, isso é até uma boa prática. É raro ter um int ou uma string perdida no sistema que não tenha um significado maior que possa ser colocada numa classe. É bem mais fácil dar manutenção depois se for uma classe do que se for um int espalhado pelo código, não é verdade?

E não é uma simples classe que só encapsula os valores, ela pode ter comportamento também. O seu paginator por exemplo poderia ser usado para cuidar dentro da paginação dos seus daos, com um método do tipo:

public class Paginator {

   public Query pagina(Query query) {
         query.setFirstResult(page*PAGE_SIZE);
                .setMaxResults(PAGE_SIZE);
         return query;
   }
}

Ou seja, não é um trabalho a mais a toa, ele tem algumas vantagens.

Oi Lucas.
De certa forma eu concordo contigo, pois temos alguns benefícios ao implementar uma classe e utilizá-la em sessão, mas em casos muito simples é mais rápido utilizar uma anotação direto no atributo, pois acho trabalhoso no sentido de quantidade de código que terei que gerar ao implementar as classes…

Em comparação, até a versão 2.6.0 podíamos utilizar as anotações @In e @Out.

O qual não era muito elegante.

Sempre achei mais fácil de entendimento e codificação, utilizar apenas uma úncia anotação, por exemplo:

@Resource  
public class UsuarioController {

...
...
	@SessionScoped
	private int pageSize;

	@SessionScoped
	private int resultSize;
...
...
	@ApplicationScoped
	private MinhaClasse atributoQualquer;
...
...
	public UsuarioController(MinhaClasse  minhaClasse, etc...) {  
		this.atributoQualquer = minhaClasse;
		this.pageSize = 30;
		this.resultSize = 0;
	}  
...
...
}

Com isso sei que eu posso enviar valores para a sessão e obtê-las a qualquer momento, pois a própria existências destas anotações sobre os atributos, faria com que o V|Raptor injetasse no Result automaticamente, já que o processo de Injeção de Dependência já esta implementado internamente.

“Veja que a própria leitura do código se torna mais simples.”

Outra coisa!!!

Acredito que aqui não seja o local correto, mas gostaria de sugerir a implementação, no V|Raptor, de uma anotação @NoTransaction, para que esta seja utilizada em lógicas que não demandem transações com o banco de dados; isto é claro, utilizando os componentes opcionais (Hibernate e JPA).
Com isso teremos um ganho de recursos e melhoria de performance, pois em determinados locais não há a necessidade de transações, por exemplo:

@Resource
public class UsuarioController {
...
...
    @NoTransaction
    @Path("/usuario/formulario")
    public void formulario() {}
...
...
}

Obrigado por toda ajuda até o momento.

Lucas_Cavalcanti

não é tão trabalhoso, vc consegue fazer isso com 1 atalho do eclipse, quer ver:

@Resource  
public class UsuarioController {

...
...
	private int pageSize;
	private int resultSize;
...
...
}

Alt + Shift + T > Extract class
Dê um nome, selecione os dois campos e aperte enter.

pronto, vc já criou a classe nova, é só recebê-la no construtor agora =)

não concordo que fica mais simples a leitura. O que pageSize e resultSize tem a ver com UsuarioController? Vou levar um tempo maior pra ver que isso
cuida da paginação do que se tivesse um atributo Paginator, não é verdade?

Falando um pouco da implementação do que vc quer fazer, os container de DI que o VRaptor usa são meio que read-only: vc registra a dependência no
começo do escopo e a instância não pode mudar no meio do caminho. Então não tem um jeito fácil de ficar gerenciando atributos e mudando o valor deles.
Com o guice e com o Spring daria para receber int ou String no construtor das classes, mas vc teria que anotar o parâmetro com @Qualifier(“resultSize”)
ou @Named(“resultSize”), o que não ia ficar mto melhor. Não é o VRaptor que instancia as classes, é o container. O VRaptor só configura.

é complicado fazer isso pq é melhor ter um @NoTransaction se a maioria das lógicas usar transações, mas se for a minoria é melhor ter um @HasTransaction, ou algo do tipo.

De qqer forma, vc pode hackear o vraptor pra usar isso :wink:
Crie a anotação @NoTransaction.
Crie a classe:

@Component // <- component mesmo, não intercepts
public class ModifiedHibernateInterceptor extends HibernateTransactionInterceptor {
     //delega o construtor

    @Override
    public boolean accepts(ResourceMethod method) {
         return method.containsAnnotation(NoTransaction.class);
    }
}

e pronto, vc pode usar o @NoTransaction nas suas lógicas =)

[]'s

softwork

Oi Lucas.

Concordo que as teclas de atalho do Eclipse ajudam muito. :slight_smile:

Em relação a facilidade de leitura de código foi no sentido da anotação e não em relação ao nome do atributo, pois ao abrir uma classe e eu ver que alguns atributos estão anotados com @SessionScoped, @ApplicationScoped, etc…, me diz que em qualquer momento eu poderei atribuir e/ou recuperar valores que estão na sessão.
O comportamento seria parecido com a versão 2.6.0 ao utilizar @In @Out.

Em relação as transações (br.com.caelum.vraptor.util.jpa) eu havia entendido que os componentes opcionais do V|Raptor (Hibernate ou JPA) abriam e fechavam transações automaticamente para TODOS os métodos/lógicas de um Controller, sendo assim, existindo uma anotação @NoTransaction, estaria dizendo para o V|Raptor que naquele método/lógica não deverá abrir e fechar transações.

Eu gostei da dica de estender HibernateTransactionInterceptor, mas como estou usando JPA deve ter uma outra (exemplo: JPATransactionInterceptor), correto ?

PS: Muito obrigado por toda paciência e explicações…

Lucas_Cavalcanti

isso, se chama JPATransactionInterceptor =)

Criado 1 de dezembro de 2010
Ultima resposta 2 de dez. de 2010
Respostas 14
Participantes 3