views:

897

answers:

2

Hi everyone,

So I'm working on this Spring MVC application using Spring Security. I've been hitting a performance problem in some instances where my controller is taking way too long to respond. This is due to a processing method that can take a huge amount of data in to process, based on some user input.

Now I've been tweaking the code a bit in and around that processing method with my team and I don't think we can get much better performance out of that without slicing it and executing each slice asynchronously.

The problem is when I try to slice it and distribute the work to child threads, using a threadpool from java.util.concurrent, I get error messages about the security context when those execute.

Here is an extract of the stacktrace:

Exception in thread "pool-1-thread-3" org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)
    at org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:63)
Exception in thread "pool-1-thread-4"   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at $Proxy63.batchSaveCampaignpayment(Unknown Source)
    at com.fim.pnp.controller.PaymentForm$1.run(PaymentForm.java:224)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
    at java.lang.Thread.run(Thread.java:595)
org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)
    at org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:63)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at $Proxy63.batchSaveCampaignPayment(Unknown Source)
    at com.fim.pnp.controller.PaymentForm$1.run(PaymentForm.java:224)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
    at java.lang.Thread.run(Thread.java:595)
Exception in thread "pool-1-thread-5" org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)
    at org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:63)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at $Proxy63.batchSaveCampaignPayment(Unknown Source)
    at com.fim.pnp.controller.PaymentForm$1.run(PaymentForm.java:224)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
    at java.lang.Thread.run(Thread.java:595)

I know it's not good practice to spawn threads from a request... but we've ran out of ideas at this point and each worker thread shouldn't take more than a handful of seconds by our measurements. Also it is expected that this feature is going to be used by 1 or 2 dedicated users once a week only.

Is there a way to pass the securityContext to the child threads or anything similar that would allow those threads to execute?

Thanks

+1  A: 

We usually do the following: In the initial spring-managed thread do

Locale locale = LocaleContextHolder.getLocale();
RequestAttributes ra = RequestContextHolder.getRequestAttributes();

Now you need to put those two values somewhere your new thread can find them. Then you do:

 LocaleContextHolder.setLocale( locale, true);
 RequestContextHolder.setRequestAttributes( ra, true);

In your new thread. Although I'm not sure if this is the supported method, it's always worked well.

krosenvold
I've tried exactly that code. made the 2 variables final so I could use them with the Threadpool. Still got the same errors. I've made sure by printing the content of the variables that they passed correctly to the child threads though.
Lancelot
+1  A: 

Maybe you could use something like InheritableThreadLocalSecurityContextHolderStrategy ? I think what it does is to copy the security context of the current thread to any threads you create within

Luciano
will give that a shot very soon... thanks.
Lancelot