Olá pessoal,
Estou com um problema para manter a transação após a chamada sucessiva de session beans.
O problema é o seguinte:
quero que ao se alterar uma entidade, seja registrado um log com as alterações. Se não for possível registrar o log, a alteração será abortada. O log só será escrito se e somente se a alteração for feita com sucesso.
Para isso usei um interceptor no método de modificar que por sua vez chama outro EJB que guarda o log. Segue o código:
A classe de repositório das entidades
@Stateful
public class RepositorioJTA implements RemoteRepositorio, LocalRepositorio {
@PersistenceContext(unitName = "agenda")
private EntityManager entityManager;
(...)
@Interceptors( { ChangeLoggerInterceptor.class })
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void modificarUsuario(Usuario usuario) {
entityManager.merge(usuario);
}
}
A classe interceptor
public class ChangeLoggerInterceptor {
@PersistenceContext(unitName = "agenda")
private EntityManager entityManager;
public ChangeLoggerInterceptor() {
}
@AroundInvoke
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Object registrar(InvocationContext context) throws Exception {
Object parametroNovo = context.getParameters()[0];
Object id = null;
Long version = null;
Method[] methods = parametroNovo.getClass().getDeclaredMethods();
int i = 0;
while ((id == null || version == null) && i < methods.length) {
methods[i].setAccessible(true);
if (methods[i].getAnnotation(Id.class) != null) {
id = methods[i].invoke(parametroNovo);
}
if (methods[i].getAnnotation(Version.class) != null) {
version = (Long) methods[i].invoke(parametroNovo);
}
i++;
}
if (version == null) {
version = new Long(0);
}
Object parametroAntigo = entityManager.find(parametroNovo.getClass(),
id);
Date d = Calendar.getInstance().getTime();
Object ref = new InitialContext().lookup("ChangeLoggerBean/remote");
ChangeLogger logger = (ChangeLogger) PortableRemoteObject.narrow(ref,
ChangeLoggerRemote.class);
logger.log("eu", d, parametroNovo, parametroAntigo, (Integer) id,
version);
return context.proceed();
}
}
O EJB que registra o log:
@Stateful
public class ChangeLoggerBean implements ChangeLoggerLocal, ChangeLoggerRemote {
@PersistenceContext(unitName="log")
public EntityManager entityManager;
public Collection<Diferenca> modificacoes(Object novo, Object antigo) {
Vector<Diferenca> itens = new Vector<Diferenca>();
Field[] fields = novo.getClass().getDeclaredFields();
for (Field f : fields) {
try {
f.setAccessible(true);
if (f.isAnnotationPresent(Log.class)
&& !f.get(novo).equals(f.get(antigo))) {
Diferenca d = new Diferenca();
Log log = (Log) f.getAnnotation(Log.class);
if (log.tipo() == TipoObjetoLog.GRAVAVEL) {
d.setClasseReferenciada(novo.getClass().toString());
d.setClasseObjeto(f.get(antigo).getClass().toString());
d.setNomeCampo(f.getName());
d.setValorAntigo(f.get(antigo).toString());
d.setValorNovo(f.get(novo).toString());
itens.add(d);
} else if (log.tipo() == TipoObjetoLog.EXPANSIVEL) {
itens.addAll(modificacoes(f.get(antigo), f.get(novo)));
}
}
} catch (IllegalAccessException e) {
} finally {
f.setAccessible(true);
}
}
return itens;
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void log(String usuario, Date modificacao, Object objetoNovo,
Object objetoAntigo, int id, long version) {
ItemChangeLog log = new ItemChangeLog();
log.setDiferencas(this.modificacoes(objetoNovo, objetoAntigo));
log.setHorario(modificacao);
log.setIdObject(id);
log.setType(objetoAntigo.getClass().toString());
log.setUser(usuario);
log.setVersion(version);
entityManager.persist(log);
}
}
O erro está acontecendo nessa ultima linha “entityManager.persist(log);”. O stacktrace é esse aqui:
Exception occurred during event dispatching:
javax.ejb.EJBTransactionRolledbackException: org.hibernate.exception.GenericJDBCException: Cannot open connection
at org.jboss.ejb3.tx.Ejb3TxPolicy.handleInCallerTx(Ejb3TxPolicy.java:87)
at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:130)
at org.jboss.aspects.tx.TxInterceptor$Required.invoke(TxInterceptor.java:195)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.TxPropagationInterceptor.invoke(TxPropagationInterceptor.java:95)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateful.StatefulInstanceInterceptor.invoke(StatefulInstanceInterceptor.java:83)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:77)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.invoke(Ejb3AuthenticationInterceptor.java:110)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:46)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateful.StatefulContainer.dynamicInvoke(StatefulContainer.java:335)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invokeLocal(IsLocalInterceptor.java:81)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:72)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateful.StatefulRemoteProxy.invoke(StatefulRemoteProxy.java:139)
at $Proxy99.log(Unknown Source)
O que é que estou fazendo errado?
Agradeço desde já a ajuda.
Thiago Jamir