Tenho uma especificação em um projeto onde tera de ser implementada auditoria.
Estou utilizando o envers para tal recurso.
Esta tudo ok.Só um ponto que não estou conseguindo utilizar.
Preciso passar qual o usuario esta sendo responsavel pela versão que está sendo registrada.
eu achei o trecho na propria documentacao do envers com outros frameworks …queria saber como pedoria fazer com o vraptor
publicclassAuditListenerimplementsRevisionListener{publicvoidnewRevision(ObjectrevisionEntity){RevEntityactualRevision=(RevEntity)revisionEntity;actualRevision.setUsername("Id do usuário");//Para o JSF eles usam geralmente ((User)FacesUtils.getManagedBean(ManagedBean.CurrentUser)).getId();//Para o Seam eles usam geralmente Identity = (Identity) Component.getInstance("org.jboss.seam.security.identity");identity.getId();//E para o vraptor como eu faria tenho um componente chamado WebSession que gerencia minha sessao e la se localiza o usuario logado.}}
O problema ta sendo recuperar essa instancia de WebSession.
G
garcia-jj
Como injetar nessa sua classe AuditListener eu não sei mesmo, nunca usei o Envers dessa forma. Mas uma coisa que eu pensei agora é que você pode usar o TransactionSynchronizationManager do Spring para "setar" o usuário via interceptor do Vraptor e depois pegar no seu AuditListener. Porém dessa forma você vai ficar amarrado ao Spring.
Talvez o Lucas tenha uma solução a fazer via vraptor.
Tentei desse modo mas chega null la no listener…tirando o fato que não implementei num interceptor…
mas faria diferença implementar no interceptor ou não???
coloquei direto no meu ponto onde logo o cara
publicvoidauthenticate(Useruser){
BooleanuserCanLogin=daoUser.userCanLogin(user);if(userCanLogin.booleanValue()){
UserlogIn=daoUser.getUserByLogin(user);logIn.setRole(daoLiberacaoAcesso.getLiberacao(logIn).getPerfil());logIn.getUnidade().getDestinatario().getCidade().getId();logIn.getRole().getActions().get(0).getId();session.setObjectSession(logIn,"user");TransactionSynchronizationManager.bindResource("user",logIn);result.redirectTo(GeneralController.class).home();}else{
result.include("error","Usuario ou senha inválidos");result.redirectTo(GeneralController.class).login();}
}
Lucas_Cavalcanti
como vc registra esse AuditListener?
em algum momento vc faz algo do tipo:
objetoMagico.register(newAuditListener());
?
a classe que tem esse código é um componente do VRaptor?
se sim, basta receber o WebSession no controller dessa classe, e na hora de registrar, vc passa no construtor do AuditListener…
[]'s
boneazul
Lucas Cavalcanti:
como vc registra esse AuditListener?
em algum momento vc faz algo do tipo:
objetoMagico.register(newAuditListener());
?
a classe que tem esse código é um componente do VRaptor?
se sim, basta receber o WebSession no controller dessa classe, e na hora de registrar, vc passa no construtor do AuditListener…
[]'s
Lucas entao eu segui a especificação do site do envers pra customizar a entidade que guarda as versões ta implementado o seguinte.
Minha entidade da revisão…
packagebr.com.webtia.contrato.models;importjavax.persistence.Column;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.GenerationType;importjavax.persistence.Id;importjavax.persistence.SequenceGenerator;importjavax.persistence.Table;importorg.hibernate.envers.RevisionEntity;importorg.hibernate.envers.RevisionNumber;importorg.hibernate.envers.RevisionTimestamp;importbr.com.webtia.contrato.audit.AuditListener;@Entity@Table(name="AUDITORIA")//Essa anotação que diz ao envenrs pra usar o meu listener acho//pelo que entendi do doc[b]@RevisionEntity(AuditListener.class)[/b]@SequenceGenerator(name="SQ_AUDITORIA",sequenceName="SQ_AUDITORIA",initialValue=1,allocationSize=1)publicclassRevEntity{@Id@GeneratedValue(strategy=GenerationType.AUTO,generator="SQ_AUDITORIA")@RevisionNumber@Column(name="AUD_ID")privateintid;@RevisionTimestamp@Column(name="AUD_TIMESTAMP")privatelongtimestamp;@Column(name="AUD_NOMEUSUARIO")privateStringusername;publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publiclonggetTimestamp(){returntimestamp;}publicvoidsetTimestamp(longtimestamp){this.timestamp=timestamp;}publicStringgetUsername(){returnusername;}publicvoidsetUsername(Stringusername){this.username=username;}}
estranho coloque um system out no construtor do StaticWebScope mas ele nao imprime nada será q não está carregando??
G
garcia-jj
Tentei desse modo mas chega null la no listener…tirando o fato que não implementei num interceptor…
mas faria diferença implementar no interceptor ou não???
Sim, faz diferença porque você precisa “setar” para poder pegar no outro lado. Apenas para conhecimento, o TransactionSynchronizationManager usa ThreadLocal para guardar os objetos que você adiciona nele. Como você colocou o bindResource no Login, na próxima requisição você perdeu as informações porque o TransactionSynchronizationManager é thread-scoped e consecutivamente request-scoped.
Quem controla o ciclo do AuditListener é o Envers que está dentro do Hibernate. Ele é na verdade um entity-listener registrado no contexto do Hibernate.
boneazul
garcia-jj:
Tentei desse modo mas chega null la no listener…tirando o fato que não implementei num interceptor…
mas faria diferença implementar no interceptor ou não???
Sim, faz diferença porque você precisa “setar” para poder pegar no outro lado. Apenas para conhecimento, o TransactionSynchronizationManager usa ThreadLocal para guardar os objetos que você adiciona nele. Como você colocou o bindResource no Login, na próxima requisição você perdeu as informações porque o TransactionSynchronizationManager é thread-scoped e consecutivamente request-scoped.
Quem controla o ciclo do AuditListener é o Envers que está dentro do Hibernate. Ele é na verdade um entity-listener registrado no contexto do Hibernate.
Pior que funcionou cara…muito obrigado pela ajuda dos dois criando o interceptor e a classe TransactionSynchronizationManager estatica do spring funcionou…vlw pela ajuda…
Muito cuidado com isso. Tem que lembrar de sempre limpar um thread-local após o término do request, senão em uma outra requisição um outro usuário pode ter a “sorte” de pegar o mesmo thread e esse objeto estará referenciado nesse thread. Nem preciso dizer a caca que isso pode dar…
Thread local é bem útil, mas tem que trabalhar com cuidado.
Lucas_Cavalcanti
@boneazul
o que está vindo null? o getScope() ou o getObjectSession?
coloque um breakpoint no método setScope do StaticWebScope, e veja se esse método está sendo chamado a toda requisição, automaticamente…
se não estiver, vc vai ter que criar um interceptor que chama esse método manualmente… =/
Lucas_Cavalcanti
Thread local é ERRADO.
é GAMBIARRA…
o problema é que quando uma biblioteca como o Envers não possibilitam que vc injete qqer objeto no
seu listener, vc tem que fazer esse tipo de coisa…
o que pode estar acontecendo tb é que o envers tá fazendo o processamento em outra thread…
@boneazul, tenta imprimir o id da thread no interceptor e dentro do seu listener, e vê se é a mesma thread…
[]'s
boneazul
Lucas Cavalcanti:
@boneazul
o que está vindo null? o getScope() ou o getObjectSession?
coloque um breakpoint no método setScope do StaticWebScope, e veja se esse método está sendo chamado a toda requisição, automaticamente…
se não estiver, vc vai ter que criar um interceptor que chama esse método manualmente… =/
bom a solução do jj-garcia rolou usando TransactionSynchronizationManager ja a sua solucao nao era chamado nunca o componente … o null estava no getScope…todo caso teria que usar um interceptor acho pra instanciar esse cara…sempre no escopo de request…mas pensando que isso é pra todo tempo que o cara estiver logado esse componente nao seria application scoped??
mas pensando no TransactionSynchronizationManager colocado uma vez como recurso…teria que me preocupar em limpar algo???
G
garcia-jj
Lucas Cavalcanti:
garcia-jj:
Thread local é bem útil, mas tem que trabalhar com cuidado.
Thread local é ERRADO.
é GAMBIARRA…
Então não olhe o que o Spring usa por baixo. É puro thread-local. Dê uma analisada nos fontes do TransactionSynchronizationManager e olhe nas classes que o usam. Aí você vê que muita coisa no Spring usa implicitamente thread-local. Não vejo o thread-local como errado, mas sim mal usado.
Lucas, o envers está no hibernate como um entity-listener. Realmente a única forma de passar para ele o usuário atual é por meios estáticos, como o exemplo que eu passei acima para JAAS. que a propósito é thread-safe. No meu caso é assim que eu uso em um projeto meu e tem funcionado muito bem.
G
garcia-jj
Não, deve ser request porque o TransactionSynchronizationManager usa internamente o thread-local, que é, a grosso modo, request-scoped. Se você não colocar ele como request você vai perder os dados na próxima requisição.
Não porque ele faz isso para você. Dê uma olhada nessa classe nos fontes do spring e você verá que ele usa thread-local e depois de acabar todo ele limpa os dados. Eu não tenho os fontes do Spring aqui e não achei o link.
Lucas_Cavalcanti
pra vc acessar via threadLocal vc teria que atualizar a instância a toda requisição, não tem o que fazer…
não conheço o TransactionSynchronizationManager, mas se ele funciona é só usar…
Vandermm
Esta auditoria serve para saber as atividades de cada usuario no site em determinado horário?
Preciso implementar algo assim. Já pesquisei e vi sugestõres para usar o envers ou log4j, mas não sei exatamente qual a forma mais apropriada.