I have deployed an Stateful EJB 3 on Wildfly that participates in a JTA transaction. This EJB is an extension User provider for Keycloak.
One of the EJB operations is invoked at some point and could produce a RuntimeException. I need the global transaction to commit so I added a try-catch block in the ejb method.
@Stateful
@Local(EjbUserStorageProvider.class)
public class EjbUserStorageProvider implements UserStorageProvider,
UserLookupProvider,
UserRegistrationProvider,
UserQueryProvider,
CredentialInputUpdater,
CredentialInputValidator,
OnUserCache
{
...
@Override
public int getUsersCount(RealmModel realm) {
try {
Object count = em.createNamedQuery("getUserCount").getSingleResult();
return ((Number)count).intValue();
} catch(RuntimeException e) {
e.printStackTrace();
return 0;
}
}
}
The problem is that, despite capturing the exception, the transaction is rolledback.
This is the problematic bit:
org.keycloak.models.utils.KeycloakModelUtils
public static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
KeycloakSession session = factory.create();
KeycloakTransaction tx = session.getTransactionManager();
try {
tx.begin();
task.run(session);
if (tx.isActive()) {
if (tx.getRollbackOnly()) {
tx.rollback();
} else {
tx.commit();
}
}
} catch (RuntimeException re) {
if (tx.isActive()) {
tx.rollback();
}
throw re;
} finally {
session.close();
}
}
For some reason tx.getRollbackOnly() returns true so the JTA transaction is rolledback.
How could I prevent the transaction from being marked as rollback only?
UPDATE
I tried using @Transactional but was ignored:
@Transactional(value = Transactional.TxType.NEVER, dontRollbackOn = {RuntimeException.class, PersistenceException.class})
@Override
public int getUsersCount(RealmModel realm) {
I also used @TransactionAttribute(NOT_SUPPORTED) but in this case Keycloak can't start:
09:36:30,784 ERROR [org.jboss.as.ejb3.invocation] (ServerService Thread Pool -- 51) WFLYEJB0034: EJB Invocation failed on component EjbUserStorageProvider for method public int es.mma.edm.keycloak.storage.user.EjbUserStorageProvider.getUsersCount(org.keycloak.models.RealmModel): javax.ejb.ConcurrentAccessTimeoutException: WFLYEJB0228: EJB 3.1 FR 4.3.14.1 concurrent access timeout on EjbUserStorageProvider - could not obtain lock within 5000 MILLISECONDS
at [email protected]//org.jboss.as.ejb3.component.stateful.StatefulSessionSynchronizationInterceptor.processInvocation(StatefulSessionSynchronizationInterceptor.java:94)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:40)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at [email protected]//org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.stateful.StatefulComponentInstanceInterceptor.processInvocation(StatefulComponentInstanceInterceptor.java:59)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInNoTx(CMTTxInterceptor.java:216)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.notSupported(CMTTxInterceptor.java:337)
at [email protected]//org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:142)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:47)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.deployment.processors.StartupAwaitInterceptor.processInvocation(StartupAwaitInterceptor.java:22)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:67)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:60)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:438)
at [email protected]//org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:619)
at [email protected]//org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:57)
at [email protected]//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at [email protected]//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at [email protected]//org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:198)
at [email protected]//org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:185)
at [email protected]//org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:81)
at deployment.edm-keycloak-user-storage.jar//es.mma.edm.keycloak.storage.user.EjbUserStorageProvider$$$view1.getUsersCount(Unknown Source)
at [email protected]//org.keycloak.storage.UserStorageManager.getUsersCount(UserStorageManager.java:453)
at [email protected]//org.keycloak.models.cache.infinispan.UserCacheSession.getUsersCount(UserCacheSession.java:543)
at [email protected]//org.keycloak.models.cache.infinispan.UserCacheSession.getUsersCount(UserCacheSession.java:548)
at [email protected]//org.keycloak.services.managers.ApplianceBootstrap.isNoMasterUser(ApplianceBootstrap.java:55)
at [email protected]//org.keycloak.services.resources.KeycloakApplication$2.run(KeycloakApplication.java:168)
at org.keycloak.keycloak-server-spi-private@4.8.3.Final-redhat-00001//org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:227)
at [email protected]//org.keycloak.services.resources.KeycloakApplication.<init>(KeycloakApplication.java:164)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at [email protected]//org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:154)
at [email protected]//org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2757)
at [email protected]//org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:363)
at [email protected]//org.jboss.resteasy.spi.ResteasyDeployment.startInternal(ResteasyDeployment.java:276)
at [email protected]//org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:88)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:119)
at [email protected]//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
at [email protected]//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:117)
at [email protected]//org.wildfly.extension.undertow.security.RunAsLifecycleInterceptor.init(RunAsLifecycleInterceptor.java:78)
at [email protected]//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:103)
at [email protected]//io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start(ManagedServlet.java:303)
at [email protected]//io.undertow.servlet.core.ManagedServlet.createServlet(ManagedServlet.java:143)
at [email protected]//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:583)
at [email protected]//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:554)
at [email protected]//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at [email protected]//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at [email protected]//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at [email protected]//io.undertow.servlet.core.DeploymentManagerImpl.start(DeploymentManagerImpl.java:596)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:97)
at [email protected]//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:78)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at [email protected]//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at [email protected]//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.base/java.lang.Thread.run(Thread.java:834)
at [email protected]//org.jboss.threads.JBossThread.run(JBossThread.java:485)
task.run(...)eventually executes the code shown here. Could there be some other piece of code higher up the stack that callstx.setRollbackOnly()? Can you check what is the result oftx.getRollbackOnly()right after the statement that throws the exception (e.g. print it in the catch block)? Finally (and perhaps unrelated), is it appropriate to catchRuntimeExceptionat that point and not a more specific exception? Why is a count query throwing? - Nikos ParaskevopoulosgetUsersCountis just getting data from the database (read operation), why not suspend the transaction when it gets invoked by changing theTransactionAttributeto not-supported? - Warren Nocos