Я генерирую случайные токены по причине, указанной в этом вопросе, которые помещаются в java.util.List
, а этот List
хранится в область сеанса.
После поиска в Google я решил удалять все элементы (токены) каждый час, содержащиеся в этом List
в сеансе.
Я мог бы подумать об использовании API Quartz, но это не представляется возможным для управление сеансом пользователя. То, что я пробовал с Quartz API (1.8.6, 2.x несовместимо с Spring 3.2, который я использую) в Spring, можно увидеть ниже.
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.");
}
}
И было настроено в файле application-context.xml
следующим образом.
<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>
Переопределенный метод в классе RemoveTokens
выполняется каждый час с начальным интервалом в 10 секунд, как настроено в XML, но невозможно выполнить какой-либо метод некоторого класса для удаления маркеров, доступных в List
, который хранится в сеансе пользователя. Является ли это возможным?
Каков честный способ удалить этот List
, хранящийся в области сеанса с определенным интервалом времени (каждый час)? Было бы намного лучше, если бы это стало возможным с помощью Quartz API.
ИЗМЕНИТЬ:
Согласно ответу ниже, я пробовал следующее, но, к сожалению, это не имело значения.
В файле application-context.xml
<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
<task:executor id="taskExecutor" pool-size="5"/>
<task:scheduler id="taskScheduler" pool-size="10"/>
Для этого требуются следующие дополнительные пространства имен,
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"
Следующий bean-компонент был зарегистрирован как 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();
}
}
}
И интерфейс, который он реализует,
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();
}
И этот бин был сконфигурирован в файле application-context.xml
следующим образом.
<bean id="sessionTokenCleanerService" class="token.SessionToken" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
Теперь я внедряю эту службу в следующий класс.
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();
}
}
А в файле application-context.xml
,
<bean id="preventDuplicateSubmissionService" class="token.PreventDuplicateSubmission"/>
Оба вышеупомянутых bean-компонента аннотированы @Service
, и они должны быть частью context:component-scan
в файле dispatacher-servelt.xml
(или как там его имя).
Метод, отмеченный аннотацией @Scheduled
в предыдущем фрагменте кода, вызывается регулярно с заданной частотой, но вызывает следующее очевидное исключение.
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
Чтобы очистить данные, хранящиеся в сеансе пользователя, метод, который выполняет эту задачу, должен вызываться регулярно через определенный интервал времени от каждого сеанса пользователя, что не относится к этой попытке. Каков путь? Вопрос может быть просто: как вызвать регулярный интервал времени для каждого сеанса пользователя? Поддерживает ли Spring или Servlet API что-то для этого?