You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by Burton Rhodes <bu...@gmail.com> on 2023/09/23 23:21:34 UTC

Issue with ExecuteAndWait when using Spring Session (Redis)

I am attempting to implement a centralized session store to an existing 
application using Spring Session (Redis) and Struts. I have everything 
working on a basic level, but I am running into an issue when a Struts 
Action uses ExecuteAndWait.  The refresh attempts don't seem to pick up 
the running action using the provided Struts Token (at least I think 
this is the case). So on each refresh, the action seems to run again as 
if it was the first time duplicating database inserts, etc.  It becomes 
an infinite process, and Struts never returns the final "SUCCESS" result 
JSP page to the browser.  It's worth noting that if I disable the Spring 
Session filter, everything works fine. Anyone have a clue as to what 
might be going on or how I might begin to troubleshoot?

I've included a thread dump so you can see the filters involved. The 
"SessionRepositoryFilter" is where the HttpSession is wrapped by Spring 
(Redis) and is before any Struts filters. I've also included a basic 
action definition that is causing the issue (although this is happening 
on all actions that use the ExecuteAndWait logic). Any help is 
appreciated.

** Example Action Definition **

<action name="ContactBatchCategories_update" 
class="com.afs.web.struts.action.contact.ContactBatchCategoriesAction" 
method="update">
     <interceptor-ref name="myDefaultStack"/>
     <interceptor-ref name="openSessionExecuteAndWaitInterceptor" >
         <param name="delay">0</param>
     </interceptor-ref>
     <result 
name="input">/struts/search/actions/contactBatchCategories_modal.jsp</result>
     <result 
name="wait">/struts/common/progressMonitorWait_modal.jsp</result>
     <result>/struts/common/progressMonitorSuccess_modal.jsp</result>
</action>


** Thread Dump **

java.lang.Thread.State: RUNNABLE
       at 
com.afs.web.struts.action.contact.ContactBatchCategoriesAction.prepare(ContactBatchCategoriesAction.java:62)
       at 
com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
       at 
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
       at 
org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:154)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
       at 
com.afs.web.config.struts.StrutsAccountInterceptor.intercept(StrutsAccountInterceptor.java:67)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
       at 
com.afs.web.config.struts.StrutsSessionInterceptor.intercept(StrutsSessionInterceptor.java:44)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
       at 
com.afs.web.common.struts.interceptor.ExceptionInterceptor.intercept(ExceptionInterceptor.java:33)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
       at 
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
       at 
org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48)
       at 
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:651)
       at 
org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79)
       at 
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.handleRequest(StrutsPrepareAndExecuteFilter.java:157)
       at 
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.tryHandleRequest(StrutsPrepareAndExecuteFilter.java:140)
       at 
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:128)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
com.afs.web.config.filter.MyAppSessionFilter.doFilter(MyAppSessionFilter.java:85)
       at 
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
       at 
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352)
       at 
org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
       at 
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)
       at 
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:110)
       at 
org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151)
       at 
org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
       at 
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
       at 
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
       at 
org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
       at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
       at 
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
       at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
       at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
       at 
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
       at 
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225)
       at 
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190)
       at 
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
       at 
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:186)
       at 
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
       at 
org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
       at 
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
       at 
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
       at 
org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210)
       at 
org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
       at 
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
       at 
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
       at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
       at 
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1570)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
       at 
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1384)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
       at 
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
       at 
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1543)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
       at 
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1306)
       at 
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
       at 
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
       at 
org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
       at 
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
       at org.eclipse.jetty.server.Server.handle(Server.java:563)
       at 
org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
       at 
org.eclipse.jetty.server.HttpChannel$$Lambda$1353.1731450897.dispatch(Unknown 
Source:-1)
       at 
org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
       at 
org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
       at 
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
       at 
org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
       at 
org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
       at 
org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
       at 
org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:416)
       at 
org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:385)
       at 
org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:272)
       at 
org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:140)
       at 
org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy$$Lambda$1338.510268842.run(Unknown 
Source:-1)
       at 
org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
       at 
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
       at 
org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
       at 
org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
       at java.lang.Thread.run(Thread.java:834)

Re: Issue with ExecuteAndWait when using Spring Session (Redis)

Posted by Burton Rhodes <bu...@gmail.com>.
Thanks Dale for the detailed responses.  I don't disagree with your 
suggestion as we making plans to migrate our platform to a React 
architecture, but our current application is robust and eliminating all 
ExecuteAndWait actions would be a monumental task - one that doesn't 
make sense in this scenario.  That said, I should have some time to 
investigate this issue further over the next few days and will report 
back.


------ Original Message ------
From "Dale Newfield" <da...@newfield.org>
To "Dale Newfield" <Da...@newfield.org>
Cc "Struts Users Mailing List" <us...@struts.apache.org>
Date 9/24/2023 12:32:08 PM
Subject Re: Issue with ExecuteAndWait when using Spring Session (Redis)

>Depending on your deployment architecture, this exec and wait may no longer be the right strategy.  Yes, by managing the executor pool you can bound how many non-http-serving threads (jobs) are executing concurrently on your web server's host, but it is all premised on using your own hardware.  If running "in the cloud", beware that this doesn't lead you to overprovison  (overspend on) always-on infrastructure...and thus limit your ability to scale dynamically for variable load requirements.
>
>These days I'd capture the data describing the pending work items somewhere (db/s3/kinesis/lots of other possibilities) and ensure some resilient scheme translates that into a broadcast message for each.  Probably subscribe a queue to that broadcast and use that to trigger the work in parallel.  If queue listeners are executing on ec2/ecs/eks, perhaps scale as that queue size grows.  Or most likely execute the steps with serverless lambdas, potentially with massive concurrency and low cost. Today there are lots of alternatives, depending on the workload: step functions, etc.   Usually you can find a way to ensure at-least-once execution of each task, so this other side of the coin is to make the work idempotent so duplicative execution cannot introduce issues.  Perhaps a DLQ for problematic jobs.  Make sure something monitors that DLQ.
>
>When exec-and-wait was originally implemented we lived in an only intermittently connected world and owned all the hardware.  I used it to sync up work done by a disconnected instance with "the real, system" upon reconnection.  (For me this meant photographers at parties, building engagement after the party by virtualizing the real world connections.  It displayed photos live during the event, and allowed assistants trailing behind the photographers to tag event-goers in those photos (by scanning barcodes on their wristbands/entering account ids/collecting email addresses on the spot to create accounts).  This allowed us to email event photos to the subjects in them by the next morning, and gave participants an easy way to connect with each other afterwards just by having a photographer take their picture together.  At the time, facial recognition was just becoming possible and was also leveraged.  But back to the technical side this meant I ran either ran a full instance (db/struts web server/etc) at the front door managing barcode assignments to match prepaid ticket holders, and supporting the whole event via wifi (I'd plug in repeaters as necessary thru the venue and/or stick battery-powered ones in the back pockets of security guards) or that I ran a full instance on the device carried around by the photographer's assistant.
>
>This was all before tablets/smartphones/social networks/etc. Today we live in a very different world--always connected, cloud based, etc.  Some tools/techniques still apply.  Many now have much better solutions (more reliable/resilient/scalable/simpler; less expensive to build/launch/operate/etc.).  YMMV.
>
>-Dale
>
>>  On Sep 23, 2023, at 9:52 PM, Dale Newfield <Da...@newfield.org> wrote:
>>
>>  If it runs again then look at the logic where it's gotta decide whether this is the initial call or if the job is already running and this  p my try just a status check.  Is it looking at the request to figure that out?  If so, and the expected info isn't found, walk the data path to figure out where it is getting lost (sounds like the session filter might be a good place to start).  Is it looking in a database/shared memory/file system?  Perhaps these are not in sync?  Perhaps the long running job is doing everything in a single transaction, not committed until the very end, and the "I'm running!" flag set within the transaction is not visible unless the query is READ_UNCOMMITTED?
>>
>>  This run/not decision is critical, so it should be determined atomically.  Perhaps one transaction for the decision (which either concludes "report status" or ensures its record is visible to any other query) and others for each separable unit of work within the long running job?
>>
>>  Oh, yeah, you said Redis.  I've somehow managed to avoid that, so I don't know what persistent data check/update can be done atomically, but that's where you're hanging your hat.  Maybe the response to your "is anyone else already running this job?" query is being cached, and so the first "no" response is being returned every time?
>>
>>  So many places this decision could go wrong in a distributed platform.  Every possible race condition will eventually happen.  Can you confidently say that two concurrent requests on distinct hosts will always result in a single "winner"?
>>
>>  (Then later on there's a "liveness" detector you'll need to figure out in case the winner fails.  Perhaps it should periodically update that record to show when it was last known to be running? (How far into the job it is right then would also be great to snapshot.)  if that time stamp is older than X, is it safe to assume it failed and to elect another leader to take its place?  Make sure time zone differences on different hosts can't impact the timeout checks.  Is it safe, even in the face of a network disconnection, so eventually the evicted leader can reappear?  Would one of them detect the issue and kill itself?)
>>
>>  -Dale
>>
>>>  On Sep 23, 2023, at 4:23 PM, Burton Rhodes <bu...@gmail.com> wrote:
>>>
>>>  I am attempting to implement a centralized session store to an existing application using Spring Session (Redis) and Struts. I have everything working on a basic level, but I am running into an issue when a Struts Action uses ExecuteAndWait.  The refresh attempts don't seem to pick up the running action using the provided Struts Token (at least I think this is the case). So on each refresh, the action seems to run again as if it was the first time duplicating database inserts, etc.  It becomes an infinite process, and Struts never returns the final "SUCCESS" result JSP page to the browser.  It's worth noting that if I disable the Spring Session filter, everything works fine. Anyone have a clue as to what might be going on or how I might begin to troubleshoot?
>>>
>>>  I've included a thread dump so you can see the filters involved. The "SessionRepositoryFilter" is where the HttpSession is wrapped by Spring (Redis) and is before any Struts filters. I've also included a basic action definition that is causing the issue (although this is happening on all actions that use the ExecuteAndWait logic). Any help is appreciated.
>>>
>>>  ** Example Action Definition **
>>>
>>>  <action name="ContactBatchCategories_update" class="com.afs.web.struts.action.contact.ContactBatchCategoriesAction" method="update">
>>>    <interceptor-ref name="myDefaultStack"/>
>>>    <interceptor-ref name="openSessionExecuteAndWaitInterceptor" >
>>>        <param name="delay">0</param>
>>>    </interceptor-ref>
>>>    <result name="input">/struts/search/actions/contactBatchCategories_modal.jsp</result>
>>>    <result name="wait">/struts/common/progressMonitorWait_modal.jsp</result>
>>>    <result>/struts/common/progressMonitorSuccess_modal.jsp</result>
>>>  </action>
>>>
>>>
>>>  ** Thread Dump **
>>>
>>>  java.lang.Thread.State: RUNNABLE
>>>      at com.afs.web.struts.action.contact.ContactBatchCategoriesAction.prepare(ContactBatchCategoriesAction.java:62)
>>>      at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
>>>      at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>>      at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:154)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>>      at com.afs.web.config.struts.StrutsAccountInterceptor.intercept(StrutsAccountInterceptor.java:67)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>>      at com.afs.web.config.struts.StrutsSessionInterceptor.intercept(StrutsSessionInterceptor.java:44)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>>      at com.afs.web.common.struts.interceptor.ExceptionInterceptor.intercept(ExceptionInterceptor.java:33)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>>      at org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48)
>>>      at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:651)
>>>      at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79)
>>>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.handleRequest(StrutsPrepareAndExecuteFilter.java:157)
>>>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.tryHandleRequest(StrutsPrepareAndExecuteFilter.java:140)
>>>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:128)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at com.afs.web.config.filter.MyAppSessionFilter.doFilter(MyAppSessionFilter.java:85)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352)
>>>      at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
>>>      at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)
>>>      at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:110)
>>>      at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151)
>>>      at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
>>>      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
>>>      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
>>>      at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
>>>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
>>>      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
>>>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
>>>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>>      at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225)
>>>      at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:186)
>>>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
>>>      at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210)
>>>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>>      at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
>>>      at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
>>>      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
>>>      at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1570)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
>>>      at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1384)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
>>>      at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
>>>      at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1543)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
>>>      at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1306)
>>>      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
>>>      at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
>>>      at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
>>>      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>>>      at org.eclipse.jetty.server.Server.handle(Server.java:563)
>>>      at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
>>>      at org.eclipse.jetty.server.HttpChannel$$Lambda$1353.1731450897.dispatch(Unknown Source:-1)
>>>      at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
>>>      at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
>>>      at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
>>>      at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
>>>      at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
>>>      at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
>>>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:416)
>>>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:385)
>>>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:272)
>>>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:140)
>>>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy$$Lambda$1338.510268842.run(Unknown Source:-1)
>>>      at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
>>>      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
>>>      at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
>>>      at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
>>>      at java.lang.Thread.run(Thread.java:834)
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
>For additional commands, e-mail: user-help@struts.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Issue with ExecuteAndWait when using Spring Session (Redis)

Posted by Dale Newfield <da...@newfield.org>.
Depending on your deployment architecture, this exec and wait may no longer be the right strategy.  Yes, by managing the executor pool you can bound how many non-http-serving threads (jobs) are executing concurrently on your web server's host, but it is all premised on using your own hardware.  If running "in the cloud", beware that this doesn't lead you to overprovison  (overspend on) always-on infrastructure...and thus limit your ability to scale dynamically for variable load requirements.

These days I'd capture the data describing the pending work items somewhere (db/s3/kinesis/lots of other possibilities) and ensure some resilient scheme translates that into a broadcast message for each.  Probably subscribe a queue to that broadcast and use that to trigger the work in parallel.  If queue listeners are executing on ec2/ecs/eks, perhaps scale as that queue size grows.  Or most likely execute the steps with serverless lambdas, potentially with massive concurrency and low cost. Today there are lots of alternatives, depending on the workload: step functions, etc.   Usually you can find a way to ensure at-least-once execution of each task, so this other side of the coin is to make the work idempotent so duplicative execution cannot introduce issues.  Perhaps a DLQ for problematic jobs.  Make sure something monitors that DLQ.

When exec-and-wait was originally implemented we lived in an only intermittently connected world and owned all the hardware.  I used it to sync up work done by a disconnected instance with "the real, system" upon reconnection.  (For me this meant photographers at parties, building engagement after the party by virtualizing the real world connections.  It displayed photos live during the event, and allowed assistants trailing behind the photographers to tag event-goers in those photos (by scanning barcodes on their wristbands/entering account ids/collecting email addresses on the spot to create accounts).  This allowed us to email event photos to the subjects in them by the next morning, and gave participants an easy way to connect with each other afterwards just by having a photographer take their picture together.  At the time, facial recognition was just becoming possible and was also leveraged.  But back to the technical side this meant I ran either ran a full instance (db/struts web server/etc) at the front door managing barcode assignments to match prepaid ticket holders, and supporting the whole event via wifi (I'd plug in repeaters as necessary thru the venue and/or stick battery-powered ones in the back pockets of security guards) or that I ran a full instance on the device carried around by the photographer's assistant.

This was all before tablets/smartphones/social networks/etc. Today we live in a very different world--always connected, cloud based, etc.  Some tools/techniques still apply.  Many now have much better solutions (more reliable/resilient/scalable/simpler; less expensive to build/launch/operate/etc.).  YMMV.

-Dale

> On Sep 23, 2023, at 9:52 PM, Dale Newfield <Da...@newfield.org> wrote:
> 
> If it runs again then look at the logic where it's gotta decide whether this is the initial call or if the job is already running and this  p my try just a status check.  Is it looking at the request to figure that out?  If so, and the expected info isn't found, walk the data path to figure out where it is getting lost (sounds like the session filter might be a good place to start).  Is it looking in a database/shared memory/file system?  Perhaps these are not in sync?  Perhaps the long running job is doing everything in a single transaction, not committed until the very end, and the "I'm running!" flag set within the transaction is not visible unless the query is READ_UNCOMMITTED?
> 
> This run/not decision is critical, so it should be determined atomically.  Perhaps one transaction for the decision (which either concludes "report status" or ensures its record is visible to any other query) and others for each separable unit of work within the long running job?
> 
> Oh, yeah, you said Redis.  I've somehow managed to avoid that, so I don't know what persistent data check/update can be done atomically, but that's where you're hanging your hat.  Maybe the response to your "is anyone else already running this job?" query is being cached, and so the first "no" response is being returned every time?
> 
> So many places this decision could go wrong in a distributed platform.  Every possible race condition will eventually happen.  Can you confidently say that two concurrent requests on distinct hosts will always result in a single "winner"?
> 
> (Then later on there's a "liveness" detector you'll need to figure out in case the winner fails.  Perhaps it should periodically update that record to show when it was last known to be running? (How far into the job it is right then would also be great to snapshot.)  if that time stamp is older than X, is it safe to assume it failed and to elect another leader to take its place?  Make sure time zone differences on different hosts can't impact the timeout checks.  Is it safe, even in the face of a network disconnection, so eventually the evicted leader can reappear?  Would one of them detect the issue and kill itself?)
> 
> -Dale
> 
>> On Sep 23, 2023, at 4:23 PM, Burton Rhodes <bu...@gmail.com> wrote:
>> 
>> I am attempting to implement a centralized session store to an existing application using Spring Session (Redis) and Struts. I have everything working on a basic level, but I am running into an issue when a Struts Action uses ExecuteAndWait.  The refresh attempts don't seem to pick up the running action using the provided Struts Token (at least I think this is the case). So on each refresh, the action seems to run again as if it was the first time duplicating database inserts, etc.  It becomes an infinite process, and Struts never returns the final "SUCCESS" result JSP page to the browser.  It's worth noting that if I disable the Spring Session filter, everything works fine. Anyone have a clue as to what might be going on or how I might begin to troubleshoot?
>> 
>> I've included a thread dump so you can see the filters involved. The "SessionRepositoryFilter" is where the HttpSession is wrapped by Spring (Redis) and is before any Struts filters. I've also included a basic action definition that is causing the issue (although this is happening on all actions that use the ExecuteAndWait logic). Any help is appreciated.
>> 
>> ** Example Action Definition **
>> 
>> <action name="ContactBatchCategories_update" class="com.afs.web.struts.action.contact.ContactBatchCategoriesAction" method="update">
>>   <interceptor-ref name="myDefaultStack"/>
>>   <interceptor-ref name="openSessionExecuteAndWaitInterceptor" >
>>       <param name="delay">0</param>
>>   </interceptor-ref>
>>   <result name="input">/struts/search/actions/contactBatchCategories_modal.jsp</result>
>>   <result name="wait">/struts/common/progressMonitorWait_modal.jsp</result>
>>   <result>/struts/common/progressMonitorSuccess_modal.jsp</result>
>> </action>
>> 
>> 
>> ** Thread Dump **
>> 
>> java.lang.Thread.State: RUNNABLE
>>     at com.afs.web.struts.action.contact.ContactBatchCategoriesAction.prepare(ContactBatchCategoriesAction.java:62)
>>     at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
>>     at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>     at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:154)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>     at com.afs.web.config.struts.StrutsAccountInterceptor.intercept(StrutsAccountInterceptor.java:67)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>     at com.afs.web.config.struts.StrutsSessionInterceptor.intercept(StrutsSessionInterceptor.java:44)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>     at com.afs.web.common.struts.interceptor.ExceptionInterceptor.intercept(ExceptionInterceptor.java:33)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>>     at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>>     at org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48)
>>     at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:651)
>>     at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79)
>>     at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.handleRequest(StrutsPrepareAndExecuteFilter.java:157)
>>     at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.tryHandleRequest(StrutsPrepareAndExecuteFilter.java:140)
>>     at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:128)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at com.afs.web.config.filter.MyAppSessionFilter.doFilter(MyAppSessionFilter.java:85)
>>     at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>     at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352)
>>     at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
>>     at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)
>>     at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:110)
>>     at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151)
>>     at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
>>     at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
>>     at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
>>     at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
>>     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
>>     at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
>>     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
>>     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>     at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>>     at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225)
>>     at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190)
>>     at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>     at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:186)
>>     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
>>     at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
>>     at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>>     at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>>     at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210)
>>     at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>>     at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
>>     at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
>>     at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
>>     at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1570)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
>>     at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1384)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
>>     at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
>>     at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1543)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
>>     at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1306)
>>     at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
>>     at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
>>     at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
>>     at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>>     at org.eclipse.jetty.server.Server.handle(Server.java:563)
>>     at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
>>     at org.eclipse.jetty.server.HttpChannel$$Lambda$1353.1731450897.dispatch(Unknown Source:-1)
>>     at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
>>     at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
>>     at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
>>     at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
>>     at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
>>     at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
>>     at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:416)
>>     at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:385)
>>     at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:272)
>>     at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:140)
>>     at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy$$Lambda$1338.510268842.run(Unknown Source:-1)
>>     at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
>>     at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
>>     at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
>>     at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
>>     at java.lang.Thread.run(Thread.java:834)

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Re: Issue with ExecuteAndWait when using Spring Session (Redis)

Posted by Dale Newfield <da...@newfield.org>.
If it runs again then look at the logic where it's gotta decide whether this is the initial call or if the job is already running and this is just a status check.  Is it looking at the request to figure that out?  If so, and the expected info isn't found, walk the data path to figure out where it is getting lost (sounds like the session filter might be a good place to start).  Is it looking in a database/shared memory/file system?  Perhaps these are not in sync?  Perhaps the long running job is doing everything in a single transaction, not committed until the very end, and the "I'm running!" flag set within the transaction is not visible unless the query is READ_UNCOMMITTED?

This run/not decision is critical, so it should be determined atomically.  Perhaps one transaction for the decision (which either concludes "report status" or ensures its record is visible to any other query) and others for each separable unit of work within the long running job?

Oh, yeah, you said Redis.  I've somehow managed to avoid that, so I don't know what persistent data check/update can be done atomically, but that's where you're hanging your hat.  Maybe the response to your "is anyone else already running this job?" query is being cached, and so the first "no" response is being returned every time?

So many places this decision could go wrong in a distributed platform.  Every possible race condition will eventually happen.  Can you confidently say that two concurrent requests on distinct hosts will always result in a single "winner"?

(Then later on there's a "liveness" detector you'll need to figure out in case the winner fails.  Perhaps it should periodically update that record to show when it was last known to be running? (How far into the job it is right then would also be great to snapshot.)  if that time stamp is older than X, is it safe to assume it failed and to elect another leader to take its place?  Make sure time zone differences on different hosts can't impact the timeout checks.  Is it safe, even in the face of a network disconnection, so eventually the evicted leader can reappear?  Would one of them detect the issue and kill itself?)

-Dale

> On Sep 23, 2023, at 4:23 PM, Burton Rhodes <bu...@gmail.com> wrote:
> 
> I am attempting to implement a centralized session store to an existing application using Spring Session (Redis) and Struts. I have everything working on a basic level, but I am running into an issue when a Struts Action uses ExecuteAndWait.  The refresh attempts don't seem to pick up the running action using the provided Struts Token (at least I think this is the case). So on each refresh, the action seems to run again as if it was the first time duplicating database inserts, etc.  It becomes an infinite process, and Struts never returns the final "SUCCESS" result JSP page to the browser.  It's worth noting that if I disable the Spring Session filter, everything works fine. Anyone have a clue as to what might be going on or how I might begin to troubleshoot?
> 
> I've included a thread dump so you can see the filters involved. The "SessionRepositoryFilter" is where the HttpSession is wrapped by Spring (Redis) and is before any Struts filters. I've also included a basic action definition that is causing the issue (although this is happening on all actions that use the ExecuteAndWait logic). Any help is appreciated.
> 
> ** Example Action Definition **
> 
> <action name="ContactBatchCategories_update" class="com.afs.web.struts.action.contact.ContactBatchCategoriesAction" method="update">
>    <interceptor-ref name="myDefaultStack"/>
>    <interceptor-ref name="openSessionExecuteAndWaitInterceptor" >
>        <param name="delay">0</param>
>    </interceptor-ref>
>    <result name="input">/struts/search/actions/contactBatchCategories_modal.jsp</result>
>    <result name="wait">/struts/common/progressMonitorWait_modal.jsp</result>
>    <result>/struts/common/progressMonitorSuccess_modal.jsp</result>
> </action>
> 
> 
> ** Thread Dump **
> 
> java.lang.Thread.State: RUNNABLE
>      at com.afs.web.struts.action.contact.ContactBatchCategoriesAction.prepare(ContactBatchCategoriesAction.java:62)
>      at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
>      at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:99)
>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>      at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:154)
>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>      at com.afs.web.config.struts.StrutsAccountInterceptor.intercept(StrutsAccountInterceptor.java:67)
>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>      at com.afs.web.config.struts.StrutsSessionInterceptor.intercept(StrutsSessionInterceptor.java:44)
>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>      at com.afs.web.common.struts.interceptor.ExceptionInterceptor.intercept(ExceptionInterceptor.java:33)
>      at com.opensymphony.xwork2.DefaultActionInvocation.executeConditional(DefaultActionInvocation.java:299)
>      at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:253)
>      at org.apache.struts2.factory.StrutsActionProxy.execute(StrutsActionProxy.java:48)
>      at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:651)
>      at org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79)
>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.handleRequest(StrutsPrepareAndExecuteFilter.java:157)
>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.tryHandleRequest(StrutsPrepareAndExecuteFilter.java:140)
>      at org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:128)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at com.afs.web.config.filter.MyAppSessionFilter.doFilter(MyAppSessionFilter.java:85)
>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352)
>      at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
>      at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131)
>      at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:110)
>      at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:101)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151)
>      at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
>      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)
>      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
>      at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:117)
>      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)
>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361)
>      at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225)
>      at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190)
>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:186)
>      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
>      at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
>      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
>      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
>      at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210)
>      at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
>      at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
>      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
>      at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
>      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>      at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
>      at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1570)
>      at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
>      at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1384)
>      at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
>      at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
>      at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1543)
>      at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
>      at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1306)
>      at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
>      at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
>      at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
>      at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
>      at org.eclipse.jetty.server.Server.handle(Server.java:563)
>      at org.eclipse.jetty.server.HttpChannel.lambda$handle$0(HttpChannel.java:505)
>      at org.eclipse.jetty.server.HttpChannel$$Lambda$1353.1731450897.dispatch(Unknown Source:-1)
>      at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:762)
>      at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:497)
>      at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
>      at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
>      at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
>      at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:416)
>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:385)
>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:272)
>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new$0(AdaptiveExecutionStrategy.java:140)
>      at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy$$Lambda$1338.510268842.run(Unknown Source:-1)
>      at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
>      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
>      at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
>      at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
>      at java.lang.Thread.run(Thread.java:834)

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org