0
Hello,
I have questions about transactional control via Demoiselle (@Transactional) in the context of handling exceptions with the Exceptionhandlerinterceptor of Demoiselle.
I have the following web service:
import br.gov.frameworkdemoiselle.transaction.Transactional;
import br.gov.frameworkdemoiselle.exception.ExceptionHandler;
import br.gov.frameworkdemoiselle.stereotype.Controller;
@WebService
@Controller
public class SislvServiceImpl implements SislvService {
@Override
@WebMethod
@Transactional
public RegistrarLaudoRetorno registrarLaudo(SolicitanteHeader solicitanteHeader, RegistroLaudoRequest laudoRequest)
throws MalformedMessage, InternalServerError, Unauthorized, LaudoRejeitado {
OperacaoRegistrarLaudo op = Beans.getReference(OperacaoRegistrarLaudo.class);
return op.registrarLaudo(solicitanteHeader, laudoRequest);
}
@ExceptionHandler
public void excecao(Exception e) throws Exception {
SislvExceptionHandler handler = Beans.getReference(SislvExceptionHandler.class);
handler.handle(e);
}
}
When executing op.registrarLaudo()
, at some point the following method will be executed so that the call to WS is properly audited:
private void auditarNoBancoSislv() {
AuditoriaRequisicaoWs auditoriaRequisicaoWs = auditoriaRequisicaoWsFactory.createAuditoriaRequisicaoWs();
auditoriaRequisicaoWsDAO.insert(auditoriaRequisicaoWs);
}
In this case, the happy way, everything goes right!
But if the method op.registrarLaudo()
triggers some exception, then we will have the treatment done by Exception Handler in Sislvserviceimpl and the execution of the method handler.handle()
. At some point the 'Handler.Handle()' method will also execute the audit method auditarNoBancoSislv()
. The problem is that as we had an exception we interrupted the execution of sislvServiceImpl.registrarLaudo()
, the transaction opened in sislvServiceImpl.registrarLaudo()
will not be effective, and thus the method auditarNoBancoSislv()
does not take effect: the audit will not be recorded in the database!
My attempt at solution was the following, change the method excecao()
for
@ExceptionHandler
@Transactional
public void excecao(Exception e) throws Exception {
SislvExceptionHandler handler = Beans.getReference(SislvExceptionHandler.class);
handler.handle(e);
}
The idea is that if an exception occurs in sislvServiceImpl.registrarLaudo()
, Demoiselle will order the rollback of this transaction (Transactionalinterceptor) and will also execute my method sislvServiceImpl.excecao()
(Exceptionhandlerinterceptor). There when the method sislvServiceImpl.excecao()
If executed, a new transaction would be opened so that we could record things in the bank, since the previous transaction would have already been closed. But it didn’t work! =(
Another attempt was the following:
@Transactional
private void auditarNoBancoSislv() {
AuditoriaRequisicaoWs auditoriaRequisicaoWs = auditoriaRequisicaoWsFactory.createAuditoriaRequisicaoWs();
auditoriaRequisicaoWsDAO.insert(auditoriaRequisicaoWs);
}
In this case the logic would be: for the exceptional path, when arriving at this in auditarNoBancoSislv()
there is no active transaction, then a new transaction opens, which did not work!!! For the happy path, Demoiselle would have to realize that there is already an active transaction, and simply do nothing for the code to take advantage of the already active transaction, which worked, although I do not know if exactly as described.
On both attempts, I wrote down with @Demoiselle Controller the classes containing the methods annotated with @Transactional. And in both cases the objects containing the methods annotated with @Transactional are instantiated via CDI.
And last but not least, man beans.xml
:
<beans ...>
<interceptors>
<class>br.gov.frameworkdemoiselle.exception.ExceptionHandlerInterceptor</class>
<class>br.gov.frameworkdemoiselle.transaction.TransactionalInterceptor</class>
</interceptors>
</beans>
Note: the rollback behavior when an exception happens is what I want for the application. Only at the time of the audit do I want to make the recording in the bank under any circumstances.
Finally, the help I need is to find a way to persist the database data by invoking the DAO on the exceptional path, which began to be executed by a method invoked by the Exceptionhandlerinterceptor of Demoiselle soon after an exception interrupted a method annotated with the Demoiselle @Transactional.
Grateful for the attention!
Leonardo Leite
=================
Later edition: new attempt.
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
public class Auditor {
@Inject
private EntityManagerFactory entityManagerFactory;
...
private void auditarNoBancoSislv() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
AuditoriaRequisicaoWsDAO auditoriaRequisicaoWsDAO = new AuditoriaRequisicaoWsDAO(entityManager);
AuditoriaRequisicaoWs auditoriaRequisicaoWs = auditoriaRequisicaoWsFactory.createAuditoriaRequisicaoWs();
entityManager.getTransaction().begin();
auditoriaRequisicaoWsDAO.insert(auditoriaRequisicaoWs);
entityManager.getTransaction().commit();
}
}
It didn’t work. Mistake:
16:22:15,319 ERROR [br.gov.serpro.siscsvws.SiscsvExceptionHandler] (http-/0.0.0.0:8443-1) Erro interno inesperado.: java.lang.IllegalStateException: A JTA EntityManager cannot use getTransaction()
at org.hibernate.ejb.AbstractEntityManagerImpl.getTransaction(AbstractEntityManagerImpl.java:1019) [hibernate-entitymanager-4.2.14.SP1-redhat-1.jar:4.2.14.SP1-redhat-1]
at br.gov.serpro.siscsvws.auditoria.Auditor.auditarNoBancoSislv(Auditor.java:48) [classes:]
Line 48 is the entityManager.getTransaction().begin();
.
Additional information, persistence.xml
:
<persistence ...>
<persistence-unit name="siscsv-ds" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/SiscsvDS</jta-data-source>
<class>br.gov.serpro.siscsv.entity.auditoria.AuditoriaRequisicaoWs</class>
....
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.default_schema" value="siscsv" />
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
</properties>
</persistence-unit>
</persistence>
Note: Demoiselle version: 2.5.0.
– Leonardo Leite