I'm generating random tokens for the reason as mentioned in this question which are placed in a java.util.List
and this List
is kept in a session scope.
After going through some Google search, I have decided to remove all elements (tokens) every hour contained by this List
in session.
I could think of using the Quartz API but doing so, doesn't seem possible for manipulating a user's session. What I have tried with the Quartz API (1.8.6, 2.x is incompatible with Spring 3.2 which I'm using) in Spring can be seen below.
package quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public final class RemoveTokens extends QuartzJobBean
{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException
{
System.out.println("QuartzJobBean executed.");
}
}
And it was configured in the application-context.xml
file as follows.
<bean name="removeTokens" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="quartz.RemoveTokens" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5" />
</map>
</property>
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="removeTokens"/>
<property name="startDelay" value="10000"/>
<property name="repeatInterval" value="3600000"/>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
The overridden method in the RemoveTokens
class is executed every hour with the initial interval of 10 seconds as configured in XML but it is not possible to execute some method of some class to remove the tokens available in the List
which is stored in a user's session. Is it possible?
What is the fair way to remove this List
stored in a session scope with a defined time interval (every hour)? It would be far better, if it were to be made possible by using this Quartz API.
EDIT:
According to the answer below, I have tried the following but unfortunately, it didn't make a difference.
In the application-context.xml
file,
<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
<task:executor id="taskExecutor" pool-size="5"/>
<task:scheduler id="taskScheduler" pool-size="10"/>
This requires the following additional namespaces,
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.2.xsd"
The following bean was registered as a session scoped bean.
package token;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
//@Scope("session")
public final class SessionToken implements SessionTokenService
{
private List<String> tokens;
private static String nextToken()
{
long seed = System.currentTimeMillis();
Random r = new Random();
r.setSeed(seed);
return Long.toString(seed) + Long.toString(Math.abs(r.nextLong()));
}
@Override
public boolean isTokenValid(String token)
{
return tokens==null||tokens.isEmpty()?false:tokens.contains(token);
}
@Override
public String getLatestToken()
{
if(tokens==null)
{
tokens=new ArrayList<String>(0);
tokens.add(nextToken());
}
else
{
tokens.add(nextToken());
}
return tokens==null||tokens.isEmpty()?"":tokens.get(tokens.size()-1);
}
@Override
public boolean unsetToken(String token)
{
return !StringUtils.isNotBlank(token)||tokens==null||tokens.isEmpty()?false:tokens.remove(token);
}
@Override
public void unsetAllTokens()
{
if(tokens!=null&&!tokens.isEmpty())
{
tokens.clear();
}
}
}
And interface which it implements,
package token;
import java.io.Serializable;
public interface SessionTokenService extends Serializable
{
public boolean isTokenValid(String token);
public String getLatestToken();
public boolean unsetToken(String token);
public void unsetAllTokens();
}
And this bean was configured in the application-context.xml
file as follows.
<bean id="sessionTokenCleanerService" class="token.SessionToken" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
Now, I'm injecting this service in the following class.
package token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public final class PreventDuplicateSubmission
{
@Autowired
private final SessionTokenService sessionTokenService=null;
@Scheduled(fixedDelay=3600000)
public void clearTokens()
{
System.out.println("Scheduled method called.");
sessionTokenService.unsetAllTokens();
}
}
And in the application-context.xml
file,
<bean id="preventDuplicateSubmissionService" class="token.PreventDuplicateSubmission"/>
Both of the above beans are annotated with the @Service
and they should be part of context:component-scan
in the dispatacher-servelt.xml
file (or whatever its name).
The method annotated with the @Scheduled
annotation in the preceding code snippet is invoked regularly at the given rate but it causes the following obvious exception to be thrown.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionTokenCleanerService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:184)
at $Proxy79.unsetAllTokens(Unknown Source)
at token.PreventDuplicateSubmission.clearTokens(PreventDuplicateSubmission.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
... 19 more
To clear data stored in a user's session, the method that does this task should be invoked regularly at a defined time interval from every user's session which is not the case with this attempt. What is the way? The question may simply be : how to trigger a regular time interval from every user's session? Does Spring or Servlet API support something to accomplish this?