I am using grails "Promise" API to run a thread.
Thread is performing the following actions
csv.splitEachLine(',') { row ->
insertRecord = Code.findByAni(row[0]) ?: new SurveyProActive(
cid: row[1],
ani: row[0],
data_time: new Date(),
dnis: Code2.findById((Long) row[2])?.dialNumber,
).save(failOnError: true, flush: true)
}
}
I am just no getting how to solve the problem I am facing .
Anyhow, I am getting the following exceptions
ERROR org.grails.web.errors.GrailsExceptionResolver - HibernateException occurred when processing request: [POST] /Survey/proactiveSurvey - parameters:
a: 17
No Session found for current thread. Stacktrace follows:
org.hibernate.HibernateException: No Session found for current thread
at org.grails.orm.hibernate.GrailsSessionContext.currentSession(GrailsSessionContext.java:116)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
at org.grails.orm.hibernate.HibernateSession.createQuery(HibernateSession.java:176)
at org.grails.orm.hibernate.HibernateSession.createQuery(HibernateSession.java:170)
at org.grails.datastore.gorm.finders.AbstractFindByFinder.buildQuery(AbstractFindByFinder.java:44)
at org.grails.datastore.gorm.finders.AbstractFindByFinder$1.doInSession(AbstractFindByFinder.java:29)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:318)
at org.grails.datastore.gorm.finders.AbstractFinder.execute(AbstractFinder.java:42)
at org.grails.datastore.gorm.finders.AbstractFindByFinder.doInvokeInternal(AbstractFindByFinder.java:27)
at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:174)
at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:374)
at org.grails.datastore.gorm.GormStaticApi.methodMissing(GormStaticApi.groovy:173)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.staticMethodMissing(GormEntity.groovy:749)
at com.ef.apps.pcs.SurveyController$_proactiveSurvey_closure4.doCall(SurveyController.groovy:334)
at org.grails.plugins.web.async.WebRequestPromiseDecorator.invokeClosure(WebRequestPromiseDecorator.groovy:43)
at org.grails.plugins.web.async.WebRequestPromiseDecorator$_decorate_closure1.doCall(WebRequestPromiseDecorator.groovy:30)
at groovyx.gpars.group.PGroup$3.call(PGroup.java:289)
at groovyx.gpars.group.PGroup$4.run(PGroup.java:313)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Check out the "Multiple Asynchronous GORM calls" section here: http://docs.grails.org/latest/guide/async.html#asyncGorm. It says that Domain.async.task{} deals with binding a new session to the asynchronous thread for you, whereas Promises.task does not.
If you are not able to use Domain.async.task{} you could also try the Domain.withNewSession{} method.
Related
In the docs it says that coroutines are lighter than threads and so I wanted to use a kotlin coroutine instead of the BukkitRunnable.
//Defined as class field
private val scope = coroutineScope(Dispatchers.Default)
//In class method
scope.launch {/* wait some seconds and then change blockdata */}
Calling setBlockData from Dispatchers.Default thread throws an error because the spigot API is not thread safe and you can't call API stuff from a thread other than the main.
java.lang.IllegalStateException: Asynchronous block remove!
I was thinking that changing block data is the equivalent of android UI changes in Minecraft which means that the coroutine needs to be run/injected into the main thread. So it would make sense to run my coroutine in Dispatchers.Main. However, I can't find a way use Dispatchers.Main and set it to the main thread without getting an illegalStateException
I hope my logic is correct here
If you want a simple method that is able to bridge the suspending code with the main thread (with the possibility of fetching some information from the main thread and use that on your coroutine), you can use this method:
suspend fun <T> suspendSync(plugin: Plugin, task: () -> T): T = withTimeout(10000L) {
// Context: The current coroutine context
suspendCancellableCoroutine { cont ->
// Context: The current coroutine context
Bukkit.getScheduler().runTask(plugin) {
// Context: Bukkit MAIN thread
// runCatching is used to forward any exception that may occur here back to
// our coroutine, keeping the exception transparency of Kotlin coroutines
runCatching(task).fold({ cont.resume(it) }, cont::resumeWithException)
}
}
}
I've commented on what context each part of the code is executed so you can visualize the context switch. suspendCancellableCoroutine is a way of getting hold of the continuation object all coroutines use under the hood, so we can manually resume it once the main thread execute our task.
The outer block withTimeout is used so that if the main thread does not complete our task within 10 seconds, our coroutine gives up instead of hanging forever.
And the use is very simple too:
val plugin = // comes from somewhere
// example coroutine scope
CoroutineScope(Dispatchers.Default).launch {
// doing stuff async
// oh no, I need some data from the main thread!
val block = suspendSync(plugin) {
// this code runs on the MAIN thread
Bukkit.getWorld("blah").getBlockAt(0, 0, 0)
}
// back to async here, do stuff with block (just don't MODIFY it async, use more suspendSync if needed)
}
If you have any questions or think I can improve this answer, don't be afraid of letting me know.
A JEE application I am looking at sometimes - no idea why it happens - goes into this wel exception.
WELD-001304: More than one context active for scope type javax.enterprise.context.SessionScoped
at org.jboss.weld.manager.BeanManagerImpl.internalGetContext(BeanManagerImpl.java:678)
at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:645)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:89)
at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:164)
at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:87)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:131)
at org.apache.deltaspike.core.impl.scope.window.WindowBeanHolder$Proxy$_$$_WeldClientProxy.getContextualStorage(Unknown Source)
at org.apache.deltaspike.core.impl.scope.window.WindowContextImpl.getContextualStorage(WindowContextImpl.java:119)
at org.apache.deltaspike.core.util.context.AbstractContext.get(AbstractContext.java:78)
at org.jboss.weld.contexts.PassivatingContextWrapper$AbstractPassivatingContextWrapper.get(PassivatingContextWrapper.java:70)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:89)
at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:87)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:131)
at org.apache.deltaspike.core.impl.scope.viewaccess.ViewAccessViewHistory$Proxy$_$$_WeldClientProxy.getLastView(Unknown Source)
at org.apache.deltaspike.core.impl.scope.viewaccess.ViewAccessContext.close(ViewAccessContext.java:131)
at org.apache.deltaspike.core.impl.scope.viewaccess.ViewAccessContext.onProcessingViewFinished(ViewAccessContext.java:119)
at org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeLifecycleWrapper.render(DeltaSpikeLifecycleWrapper.java:118)
at javax.faces.lifecycle.LifecycleWrapper.render(LifecycleWrapper.java:92)
at org.apache.deltaspike.jsf.impl.listener.request.JsfClientWindowAwareLifecycleWrapper.render(JsfClientWindowAwareLifecycleWrapper.java:160)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:659)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
It is not clear why this is happening.
And the side effects are not always the same.
Some times when this situation happens, you really need to restart the server, eve if you try to loging in incognito mode with a different browser.
It is as if the BeanManager from WELD has become completely toasted.
This error is coming most often when you sleep your computer and the next day you start interacting with the application.
But interesting enough, this is also happening quite often if I start triggering selinum tests.
Have no idea why the selenium testing would exacerbate the exception.
What I have seen now by putting a break point, is that we have WELD trying to resolve some injection point annotations.
At some point, one of the injeaction points it wants to resolve is a:
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:87)
It is doing a
T existingInstance = ContextualInstance.getIfExists(bean, manager);
Where the Managed Bean
parameter bean = [class org.apache.deltaspike.core.impl.scope.window.WindowBeanHolder] with qualifiers [#Any #Default]
It is tring to resolve the bean injext:
[[BackedAnnotatedField] #Inject private org.apache.deltaspike.core.impl.scope.window.WindowBeanHolder.windowContextQuotaHandler]
Into the bean:
Managed Bean [class org.apache.deltaspike.jsf.impl.scope.window.JsfWindowContextQuotaHandler] with qualifiers [#Any #Default]
This window bean holder (could be any other bean I believe, it is irrelevant) is annotated with the session scoped annotation:
#SessionScoped
public class WindowBeanHolder extends AbstractBeanHolder<String>
{
And the system then breaks because the wildfly beanmanager impl, when it enter the logic of returning the SessionScoped context at line 678:
private Context internalGetContext(Class<? extends Annotation> scopeType) {
Context activeContext = null;
final List<Context> ctx = contexts.get(scopeType);
if (ctx == null) {
return null;
}
for (Context context : ctx) {
if (context.isActive()) {
if (activeContext == null) {
activeContext = context;
} else {
throw BeanManagerLogger.LOG.duplicateActiveContexts(scopeType.getName());
}
}
}
return activeContext;
}
This BeanManagerImpl code will not be happy with the fact that apparently a two SessionScopedContexts are at the same active.
In this code fragment what I see with the debugger is that the contexts variable is holding the following 3 session scoped contexts:
[
org.jboss.weld.contexts.bound.BoundSessionContextImpl#136b5782,
org.jboss.weld.module.web.context.http.HttpSessionContextImpl#6ef334ee,
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext#37a5a86e
]
The first ACTIVE CONTEXT found was:
oactiveContext = rg.jboss.weld.module.web.context.http.HttpSessionContextImpl#6ef334ee
The second CONTEXT we have is:
context = org.jboss.weld.module.web.context.http.HttpSessionDestructionContext#37a5a86e
That means for whatever reason, in my seystem both the HttpSessionContextImpl and the HttpSessionDestructionContext.
I am clueless is a to how these contexts are being toggled between ACTIVE/INACTIVE, if this is supposed to be "ThreadContext" specific flag that for a given thread the session scoped is corrupted but other threads with differ JESSESIONID cookie would start in an appropriate virgin state. where only one session context is active.
Any ideas of what could be causing this?
Note, widlfly 13.0.Final uses:
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-core-impl</artifactId>
<version>3.0.4.Final</version>
<scope>provided</scope>
</dependency>
Many thanks
The issue is now understood.
This duplicate active session scoped context is caused by two factors.
First an application specific problem that only occurs in widlfy and not in weblogic.
Second a wildfly problem (this one is debatable it is a matter of opinion, but I believe wildfly should be more robust here).
The first problem.
Whenever an HTTP session becomes expired in wildfly / undertow, a session reaper process will kick in to terminate the session. Every application server has its own process of doing this.
The following stack trace snippet depicts what is going on when a session is being destoryed in wildfly.
####2019-11-19 16:40:49,522 ThreadId:441 ERROR org.jboss.threads.errors - Thread Thread[default task-4,5,main] threw an uncaught exception: java.lang.RuntimeException: org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
at io.undertow.servlet.core.SessionListenerBridge.sessionDestroyed(SessionListenerBridge.java:75)
at io.undertow.server.session.SessionListeners.sessionDestroyed(SessionListeners.java:61)
at io.undertow.server.session.InMemorySessionManager$SessionImpl.invalidate(InMemorySessionManager.java:586)
at io.undertow.server.session.InMemorySessionManager$SessionImpl$2$1.run(InMemorySessionManager.java:393)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.jboss.weld.contexts.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped
at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:647)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:89)
at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:164)
at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:87)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:131)
at org.apache.deltaspike.core.impl.scope.window.WindowIdHolder$Proxy$_$$_WeldClientProxy.getWindowId(Unknown Source)
at org.apache.deltaspike.core.impl.scope.window.WindowContextImpl.getCurrentWindowId(WindowContextImpl.java:85)
at org.apache.deltaspike.core.impl.scope.window.InjectableWindowContext.getCurrentWindowId(InjectableWindowContext.java:54)
at com.XXXXX.YYYYY.framework.web.util.JsfUtilBean.getWindowId(JsfUtilBean.java:230)
at com.XXXXX.YYYYY.framework.web.util.JsfUtilBean$Proxy$_$$_WeldClientProxy.getWindowId(Unknown Source)
at com.XXXXX.YYYYY.framework.web.enterprisetouch.boxframework.push.SomeIrrlevantAppSpecificClass.logout(SomeIrrlevantAppSpecificClass.java:105)
at com.XXXXX.YYYYY.framework.web.enterprisetouch.boxframework.push.SomeIrrlevantAppSpecificClass$Proxy$_$$_WeldClientProxy.logout(Unknown Source)
at com.XXXXX.YYYYY.framework.web.client.YYYYYSessionController.sessionDestroyed(YYYYYSessionController.java:245)
at com.XXXXX.YYYYY.framework.web.client.YYYYYSessionController$Proxy$_$$_WeldClientProxy.sessionDestroyed(Unknown Source)
at com.XXXXX.YYYYY.framework.web.security.YYYYYSessionListener.sessionDestroyed(YYYYYSessionListener.java:80)
at io.undertow.servlet.core.ApplicationListeners.sessionDestroyed(ApplicationListeners.java:315)
at io.undertow.servlet.core.SessionListenerBridge.doDestroy(SessionListenerBridge.java:98)
at io.undertow.servlet.core.SessionListenerBridge.access$000(SessionListenerBridge.java:41)
at io.undertow.servlet.core.SessionListenerBridge$1.call(SessionListenerBridge.java:54)
at io.undertow.servlet.core.SessionListenerBridge$1.call(SessionListenerBridge.java:51)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1514)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1514)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1514)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1514)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1514)
at io.undertow.servlet.core.SessionListenerBridge.sessionDestroyed(SessionListenerBridge.java:73)
... 8 more
The above stack trace is important for two reasons.
One it gives you an idea of how underdow decides to initiate killing of sessions that have timed out.
Second it is showing you a very real possibility on any application - it can break when handling a session destruction event.
The code that is blowing up the stack trace above works perfectly fine in weblogic, because we have a RequestScope active.
The code in wildfly is breaking because there is no request scope active.
So I have not fixed the code blowing up in the stack trace above yet because I wanted to find a hack to get myself out of the problem that was reported here - since this could happen agian in the future very easily.
So for me the request scope exception is irrelevant, what is relevat are the side effects that come after.
It is also very important to understand that before the code that is blowing up above was called something very important took place.
Namely the activation of what will be in the future the SECOND / DUPLICATION session scope context.
Please look at the stack trace that I will put bellow.
This stack trace takes place whenever UNDERTOW decides to initiate killing of sessions.
When this happens wildfly comes int and decides to activate the very special session scope context implementation that is used outside of HTTP requests.
-------------------
-- HTTP SESSION - DEACTIVATION:
---------------------
Thread [default task-102] (Suspended (breakpoint at line 41 in org.jboss.weld.contexts.AbstractManagedContext))
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext(org.jboss.weld.contexts.AbstractManagedContext).setActive(boolean) line: 41
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext(org.jboss.weld.contexts.AbstractManagedContext).activate() line: 49
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext(org.jboss.weld.contexts.AbstractBoundContext<S>).activate() line: 66
org.jboss.weld.module.web.servlet.WeldTerminalListener.sessionDestroyed(javax.servlet.http.HttpSessionEvent) line: 95
io.undertow.servlet.core.ApplicationListeners.sessionDestroyed(javax.servlet.http.HttpSession) line: 315
io.undertow.servlet.core.SessionListenerBridge.doDestroy(io.undertow.server.session.Session) line: 98
io.undertow.servlet.core.SessionListenerBridge.access$000(io.undertow.servlet.core.SessionListenerBridge, io.undertow.server.session.Session) line: 41
io.undertow.servlet.core.SessionListenerBridge$1.call(io.undertow.server.HttpServerExchange, io.undertow.server.session.Session) line: 54
io.undertow.servlet.core.SessionListenerBridge$1.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: 51
io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(io.undertow.server.HttpServerExchange, C) line: 42
io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(io.undertow.server.HttpServerExchange, C) line: 43
org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 105
org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction$$Lambda$764.660751084.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 1514
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$765.1975131456.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 1514
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$765.1975131456.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 1514
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$765.1975131456.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 1514
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$765.1975131456.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(io.undertow.servlet.api.ThreadSetupHandler$Action, io.undertow.server.HttpServerExchange, java.lang.Object) line: 1514
org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction$$Lambda$765.1975131456.call(io.undertow.server.HttpServerExchange, java.lang.Object) line: not available
io.undertow.servlet.core.SessionListenerBridge.sessionDestroyed(io.undertow.server.session.Session, io.undertow.server.HttpServerExchange, io.undertow.server.session.SessionListener$SessionDestroyedReason) line: 73
io.undertow.server.session.SessionListeners.sessionDestroyed(io.undertow.server.session.Session, io.undertow.server.HttpServerExchange, io.undertow.server.session.SessionListener$SessionDestroyedReason) line: 61
io.undertow.server.session.InMemorySessionManager$SessionImpl.invalidate(io.undertow.server.HttpServerExchange, io.undertow.server.session.SessionListener$SessionDestroyedReason) line: 586
io.undertow.server.session.InMemorySessionManager$SessionImpl$2$1.run() line: 393
org.jboss.threads.ContextClassLoaderSavingRunnable.run() line: 35
org.jboss.threads.EnhancedQueueExecutor.safeRun(java.lang.Runnable) line: 1985
org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(java.lang.Runnable) line: 1487
org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run() line: 1378
java.lang.Thread.run() line: 748
This stack trace here is critical to understand.
So normally, on what we can call an happy HTTP request, widlfly will activate the following session scope context class.
org.jboss.weld.module.web.context.http.HttpSessionContextImpl(org.jboss.weld.contexts.AbstractManagedContext).setActive(boolean) line: 41
But as shown above, when it is time to kill of an http session, then instead it activates that other implementation class of a session scope the. The
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext(org.jboss.weld.contexts.AbstractManagedContext).setActive(boolean) line: 41
So until here is the story clear?
Let us summarize until here.
(a) We have our application running happy
(b) We close our browser window and we let the http session timeout
(c) When undertow kills of the http session wildfly will acttivate the context HttpSessionDestructionContext
(d) Our application speific session listener that is curious about ending sessions will blow up because the request scope context is not active
(e) ... I suppose the story finishes up very badly, because wildfly would most likely have some nice class or logic to in the end of proper session termination to deactivate the HttpSessionDestructionContext but whatever this logic is and whatever makes it gets triggered, for this application it will NEVER get triggered.
So what are we left with at this point?
With a very subtle bug.
We finally have a very corrupted thread.
Whatever thread undertow used to kill of the session is now forever doomed to not support any more the session scoped context.
Why?
Because when you look at the implementation of these session contexts, you will see their state of being ative or not active is a THREAD LOCAL variable.
What this means is for as long as this thread lives, the state it had fro the previous run will remain.
And here is where Wildfly is having a problem, in my opinion.
Widlfly could either have a "pre-emptive" cleanup logic that tries to make sure that before a thread is used to handover a request the thread local variables are cleaned up to avoid going on ahead with a dirty thread.
Or it would need to have some sort of resilience mechanism to make sure that when it activates a scope in the context of a running thread, before the thread finishes well or bad, that the scope is ultimately deactivated.
Ok.
To finalize.
So now we have an application that blew up and as reward this applicaton now has one thread that is toasted.
This issue is normally very difficult to reproduce because you normally do not have a very low timeout.
So you have the feeling that this issue comes when you leave your applicaton server running for extended period time. Like you go home, put your computer to sleep and the next morning everything is blowing up (because all of your http sesisons have timed out since then).
The best way to make this issue reproducible, is to go t your standalone.xml and set it up like this:
<servlet-container name="default" default-session-timeout="1">
<jsp-config/>
<websockets/>
</servlet-container>
You set the timeout of a session to run every minute.
Then with chrome you just open an icognito window loging to your application to get the JESSIONID created and close the window.
Open a different iconito window login again and close again.
Repeat this for many sessions.
Then in parallel, what you can do as well, is make sure that you set a DEBUG BREAKPOINT on the
org.jboss.weld.contexts.AbstractManagedContext
Put the break point on the method:
protected void setActive(boolean active) {
getManagedState().setActive(active);
}
Put in there the following breakpoint condition:
this.getClass().getName().contains("HttpSessionDestructionContext")
What this break point will allow you to do is to make sure that when UNDERTOW starts killing of your sessions, you block the thread and that forces the killing process of the sessions to go over multiple different threds.
The larger the number of different threads that have been used to terminate sessions, the more threads you have corrupted, the more likely you are to re-use the thread in the future in a normal http request that will blow up with this error.
Essentially, you are just simulating what in a normal production server might take hours to happen.
In a productive server a session can be active for many hours if a user is active, unitl he goes home or whatever.
And the next day is when you start having threads in your thread pool that are unsuable.
Ok to finalize.
Soon I will be fixing that code that wsa breaking due to the lack request scope context.
But before that, I want to make sure i am somehow able to heal my widlfly threads in case in the future this situation happes again.
To do this I am using a servlet listener as shown bellow.
(the usage of listener and helper as separate classes has to do with now having the WAR file blow up when you try to deploy on weblogic and weblogic might complain it has no idea of what this org.jboss.weld.module.web.context.http.HttpSessionDestructionContext is about).
package wahteverpackage;
import java.util.Arrays;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import LALALLA.commons.util.constants.CommonsConstants;
import LALALLA.commons.util.util.BasicApplicationServerIdentificationUtil;
#ApplicationScoped
public class LALALLAWildflyWELD001304ServletRequestListener implements ServletRequestListener {
/**
* The issue takes place after an http session is timed out and a subsequent http request is handled by the thread
* that managed the timeout that thread is corrupted. So we want to fix the corrupted thread that is handling na
* http request.
*/
private static final List<String> RELEVANT_SERVLET_REQUEST_PROTOCOLS = Arrays.asList("http", "https");
#Inject
BeanManager beanManager;
final String appServerName;
final Boolean isWildfly;
/**
* Create a new LALALLAWildflyWELD001304ServletRequestListener.
*
*/
public LALALLAWildflyWELD001304ServletRequestListener() {
super();
appServerName = BasicApplicationServerIdentificationUtil.getApplicationServerName();
isWildfly = CommonsConstants.WILDFLY.equals(appServerName);
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
// on request destroyed we do not care to do no anything
}
#Override
public void requestInitialized(ServletRequestEvent sre) {
// (a) We only want this code to have any effect if it is running in wildfly
if (!isWildfly) {
return;
}
// (a) Make sure we are dealing with an http servlet request
if (!isHttpServletRequest(sre)) {
return;
}
// (b) We only want to intervene in the request if the current thread
// be broken by the multiple contexts active exception
LALALLAWildflyWELD001304ServletRequestListenerHelper helper = new LALALLAWildflyWELD001304ServletRequestListenerHelper();
if (!helper.isCurrentThreadCorruptedWithWELD001304Exception(beanManager)) {
return;
}
helper.tryToRepairCorruptedThread(beanManager);
}
/**
* Check if we are dealing with an http servlet request.
*
* #param sre
* the servlet request
* #return TRUE if we are dealing with an http servlet request
*/
protected boolean isHttpServletRequest(ServletRequestEvent sre) {
return RELEVANT_SERVLET_REQUEST_PROTOCOLS.contains(sre.getServletRequest().getScheme().toLowerCase());
}
}
Finally the helper class that was isolated out to be able to deploy on weblogic and that is doing the vodoo of trying to repair the thread is the following.
package wahteverpackage;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.spi.BeanManager;
import javax.servlet.http.HttpSession;
import org.jboss.weld.contexts.AbstractBoundContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import LALALLALALALLA.commons.util.cdi.CdiUtil;
/**
* This listener relates to the issue
*
* LALALLAMaint-3541 - Wildfly - WELD-001304 - More than one context active for scope type
* javax.enterprise.context.SessionScoped</a>
*
* We want to create a listener to tries to avoid the problem that once in wildlfy the HttpSessionDestructionContext is
* activated during a session timeout, if the session timeout event blows up, the thread that processed the session
* timeout will have its thread local state variables corrupted always activating this context implementation.
*/
public class LALALLAWildflyWELD001304ServletRequestListenerHelper {
private static final Logger LOGGER = LoggerFactory
.getLogger(LALALLAWildflyWELD001304ServletRequestListenerHelper.class);
/**
* Try to access the {#link SessionScoped} context to see if our thread will be damaged by a
*
* {#code org.jboss.weld.exceptions.IllegalStateException: WELD-001304: More than one context active for scope type javax.enterprise.context.SessionScoped}
*
* #return TRUE if the session scope for this specific thread has been corrupted. The corruption is most likely
* derived to a session timeout explosion with the
* {#link LALALLALALALLA.framework.web.security.LALALLASessionListener} blowing up when the sessionDestroyed
* is called.
*/
public boolean isCurrentThreadCorruptedWithWELD001304Exception(BeanManager beanManager) {
try {
// usual
beanManager.getContext(SessionScoped.class);
return false;
} catch (Exception e) {
org.jboss.weld.exceptions.IllegalStateException duplicateActiveContextsExceptionExample = org.jboss.weld.logging.BeanManagerLogger.LOG
.duplicateActiveContexts(SessionScoped.class.getName());
if (duplicateActiveContextsExceptionExample.getMessage().equals(e.getMessage())) {
// We are hiting a thread that has been corrupted and the session context for this thread
// as two implementation of the session scoped context currently active
return true;
}
return false;
}
}
/**
* The try to repair corrupted thread is concerned with the fact that the thread local memory of the current thread
* is stating that the HttpSessionDestructionContext is active, and this memory state is not being cleared because
* when the session timed out the process of handling the session timeout was interrupted due to some blow up
* exception.
*
* To fix this of course the code that is blowing up should be repaired. But to be on the safe side we have this
* work around code that will try to heal the thred and put it back to to a workable state.
*
*/
public void tryToRepairCorruptedThread(BeanManager beanManager) {
LOGGER.error("The current thread appears to be corrupted facing the"
+ " WELD-001304: More than one context active for scope type javax.enterprise.context.SessionScoped."
+ " See https://stackoverflow.com/questions/58930939/wildflt-13-weld-001304-more-than-one-context-active-for-scope-type-javax-enterp "
+ " It is posisble that this thread has been used in the past to handle an http session timeout that blew up during the "
+ " io.undertow.servlet.core.ApplicationListeners.sessionDestroyed phase. "
+ " If this is the case it is very likely that the thread local memory of state of the current thread is corrupted"
+ " with the wildfly HttpSessionDestructionContext stating that it is active when it should not be active at all."
+ " We will try to deactive this context. ");
AbstractBoundContext<HttpSession> httpSessionDestructionContext = getSessionDestructionContext(beanManager);
if (httpSessionDestructionContext.isActive()) {
LOGGER.warn(
"Our assumtion that the problem is that the wildfly HttpSessionDestructionContext is active is true. We will now try to cleanup the thread local of this thread by "
+ " forcing this HttpSessionDestructionContext to deactivate ");
httpSessionDestructionContext.deactivate();
} else {
LOGGER.warn(
"The situation is not clear. two or more contexts are active for the session context, our expectation is that the two activate contexts are the org.jboss.weld.context.http.HttpSessionContext and the org.jboss.weld.module.web.context.http.HttpSessionDestructionContext "
+ " but the HttpSessionDestructionContext seems not to be ACTIVE. ");
}
}
/**
* The HttpSessionDestructionContext obtained using the same technique as HttpContextLifecycle.
*
* #return The HttpSessionDestructionContext that we have seen to be active in requests where it should not be
* active.
*/
// Ignore the fact that we are using fully qualified names
// we want to be able to deploy this java class to weblogic
// without getting explosions that imports at the class level are not prese
#SuppressWarnings("squid:S1942")
public AbstractBoundContext<HttpSession> getSessionDestructionContext(BeanManager beanManager) {
// In a cdi container we get injected a a proxy to the bean manager
// in org.jboss.weld.module.web.servlet.HttpContextLifecycle is getting the beanMangerImpl directly
// and using a different technique to get the HttpSessionDestructionContext using the same approach as the
return CdiUtil.getBeanByClass(beanManager,
org.jboss.weld.module.web.context.http.HttpSessionDestructionContext.class);
}
}
Finally, I will probably be opening an issue in :
https://issues.jboss.org/projects/WFLY/issues
Thanks for all the help.
I want to verify exception throwing in one of my running threads. This is piece of my test code:
then:
def e = thrown(RequestFormatException)
e.message == "Incorrect first line: INCORRECT LINE"
When I run this I get next messages:
Exception in thread "Thread-1" by.westside.staircase.core.exception.RequestFormatException: Incorrect first line: INCORRECT LINE
at by.westside.staircase.core.util.HttpUtil.parseHttpRequest(HttpUtil.kt:19)
at by.westside.staircase.core.server.ServerThread.run(ServerThread.kt:26)
at java.lang.Thread.run(Thread.java:745)
Expected exception of type 'by.westside.staircase.core.exception.RequestFormatException', but no exception was thrown
at org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:79)
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:66)
at by.westside.staircase.core.server.SyncServerSpec.should throw exception in incorrect first line case(SyncServerSpec.groovy:26)
Spock, like JUnit, can only assert on exceptions thrown from the thread executing the test, not "any thread in the application". Your exceptions are not caught by spock, and can't be asserted.
You can play with Thread.uncaughtExceptionHandler but you should probably unit-test the runnable executed in your thread - or implement some error handling in your business logic, and test this part of the code.
I think another option is to actually catch the exception in your test case and assert on that. here is a snippet of my code (written in Groovy Spock):
def exceptionThrown = false
def exceptionMessage
def thread = new Thread( {_ ->
try{
//code you are testing
} catch(Exception e) { // change this to the exception you want to catch
exceptionThrown = true
exceptionMessage = e.getMessage()
}
})
then: "the right exception should be thrown"
exceptionThrown
exceptionMessage = "I am thrown" //this should be your error message
I ran into the same issue and took a simple, hokey route. In the spirit of "good software is testable software" I added a flag and asserted on that, labeling it: // only for testing. Which, of course, will be ignored down the road.
thrown(RequestFormatException)
this should be in your first line after then: as this is the constraint imposed by spock.
Whenever thrown or notThrown is called in it should be the first statement.
Note: thrown and notThrown both return true and hence there should be no comparison operator as well.
Hence, In your case , it should be like below:
then:
thrown(RequestFormatException)
I'm using Spark Streaming to process a stream by processing each partition (saving events to HBase), then ack the last event in each RDD from the driver to the receiver, so the receiver can ack it to its source in turn.
public class StreamProcessor {
final AckClient ackClient;
public StreamProcessor(AckClient ackClient) {
this.ackClient = ackClient;
}
public void process(final JavaReceiverInputDStream<Event> inputDStream)
inputDStream.foreachRDD(rdd -> {
JavaRDD<Event> lastEvents = rdd.mapPartition(events -> {
// ------ this code executes on the worker -------
// process events one by one; I don't use ackClient here
// return the event with the max delivery tag here
});
// ------ this code executes on the driver -------
Event lastEvent = .. // find event with max delivery tag across partitions
ackClient.ack(lastEvent); // use ackClient to ack last event
});
}
}
The problem here is that I get the following error (even though everything seems to work fine):
org.apache.spark.SparkException: Task not serializable
at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:166)
at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:158)
at org.apache.spark.SparkContext.clean(SparkContext.scala:1435)
at org.apache.spark.rdd.RDD.mapPartitions(RDD.scala:602)
at org.apache.spark.api.java.JavaRDDLike$class.mapPartitions(JavaRDDLike.scala:141)
at org.apache.spark.api.java.JavaRDD.mapPartitions(JavaRDD.scala:32)
...
Caused by: java.io.NotSerializableException: <some non-serializable object used by AckClient>
...
It seems that Spark is trying to serialize AckClient to send it to the workers, but I thought that only code inside mapPartitions is serialized/shipped to the workers, and that the code at the RDD level (i.e. inside foreachRDD but not inside mapPartitions) would not be serialized/shipped to the workers.
Can someone confirm if my thinking is correct or not? And if it is correct, should this be reported as a bug?
You are correct, this was fixed in 1.1. However, if you look at the stack trace, the cleaner that is throwing is being invoked in the mapPartitions
at org.apache.spark.SparkContext.clean(SparkContext.scala:1435)
at org.apache.spark.rdd.RDD.mapPartitions(RDD.scala:602)
So, the problem has to do with your mapPartitions. Make sure that you aren't accidentally wrapping this, as that is a common issue
I'm trying to set a textview's text from other thread than the main one,so I've written in the constructor:
Thread myth = new Thread (new ThreadStart (set_txt));
myth.Start ();
and of course set_txt is a method contains
textview1.Buffer.Text = "Whatever";
The probleme is that When I run the code most time it stops and gives an error:
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
What should I do??
You need to update the GTK# text view from the GUI thread. You can do this by using Gtk.Application.Invoke:
Gtk.Application.Invoke (delegate {
textview1.Buffer.Text = "Whatever";
});
You need to update the UI from the UI thread. Just use Gtk.Application.Invoke passing it a lambda or a delegate:
Gtk.Application.Invoke(() => { textview1.Buffer.Text = "Whatever"; });