Quartz任务调度,访问Servlet Context容器中的数据
2014年08月07日 18:55:37
阅读数:1102
Quartz是一种功能丰富的开源作业调度库,它可以在几乎任何Java应用程序集成,从最小的单机应用到最大的电子商务系统。 Quartz可以用来创建简单或复杂的任务,调度执行数以十计,数百计,甚至成千上万的任务。这些拥有某种Task的Job被定义为标准的Java组件,可以执行几乎任何你可以编程实现的事情。 Quartz调度包括了许多企业级功能,如JTA事务和集群支持。
Quartz是可免费使用,根据Apache2.0许可证授权。
官网地址:Quartz
问题背景:
因为做项目需要,对于登录次数超过一定数量的用户,系统要判定为恶意登录,应该对那个账号暂时性的锁定,锁定时间内此账号是不能再次进行登录请求的,这样可以有效减轻恶意登录情况。
之前想到的一个简单办法是session中计数,但是后来立马被自己否定了,原因有几个:
用户换一个浏览器session值就改变了,也就可以再次用被锁定的账号;
即便如此,session失效的时间是系统启动时就配置好了,不可控,比如我想锁定时间设置成3个小时。
解决方案:
采用Servlet Context上下文保存,用一个Map保存锁定用户信息,key为用户名,value为锁定开始时间,然后把Map保存在Servlet上下文中。采用Quartz任务调度定时扫描锁定列表,将达到解锁时间的用户移除。
因为之前未深入了解Quartz,遇到了一个问题,就是不知道怎么让任务中访问到Servlet Context对象,spring和Quartz继承后,在配置文件中定义的调度任务也没法访问到Servlet 上下文(至少是在我写此博文时,我还没想来)。查阅了官网使用文档,介绍的任务都是较为简单不用和Servlet上下文交互。x网上介绍Quartz和Servlet交互的博客不多,于是自己花时间研究源代码。
org.quartz.ee.servlet.QuartzInitializerListener或者org.quartz.ee.servlet.QuartzInitializerServlet类,原话是这么说的——A ServletContextListner that can be used to initialize Quartz.,也就是说采用ServletContextListner或者HttpServlet这两种方式来初始化Quartz调度器。
我在项目中采用的是第一种方式,继承QuartzInitializerListener监听器类,具体如下:
-
<span style="font-family:Microsoft YaHei;font-size:14px;">public class MyQuartzContextListener extends QuartzInitializerListener {
-
static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);
-
@Override
-
public void contextInitialized(ServletContextEvent sce) {
-
super.contextInitialized(sce);
-
ServletContext servletContext = sce.getServletContext();
-
//scheduler factory
-
StdSchedulerFactory sFactory = (StdSchedulerFactory)servletContext.getAttribute(QUARTZ_FACTORY_KEY);
-
Scheduler scheduler = null;
-
try {
-
scheduler = sFactory.getScheduler();
-
//定义一个JobDetail
-
JobDetail jobDetail = new JobDetail("lockedUserJobDetail", "lockedUserGroup", LockedUserMonitor.class);
-
// 将ServletContext对象放到map中,然后从job中取出来,从而取得路径
-
Map<String, Object> map = new HashMap<String, Object>();
-
map.put("servletContext", servletContext);
-
//将servlet上下文添加到JobDataMap中
-
JobDataMap dateMap = new JobDataMap(map);
-
jobDetail.setJobDataMap(dateMap);
-
//触发器
-
Trigger trigger = new CronTrigger("lockedUserCronTrigger", "lockedUserCronTrigger", "0 0/1 * ? * *");
-
//关联任务和触发器
-
scheduler.scheduleJob(jobDetail, trigger);
-
//开启调度
-
scheduler.start();
-
} catch (SchedulerException e) {
-
logger.error("调度器 MyQuartzContextListener", e);
-
e.printStackTrace();
-
} catch (ParseException e) {
-
logger.error("调度器 CronTrigger表达式解析错误", e);
-
e.printStackTrace();
-
}
-
}
-
}</span>
在web.xml配置文件添加
-
<span style="font-family:Microsoft YaHei;font-size:14px;"><!-- 任务调度监听器 -->
-
<listener>
-
<listener-class>com.marketing.listener.MyQuartzContextListener</listener-class>
-
</listener></span>
然后是任务具体实现类,实现org.quartz.job.Job接口
-
<span style="font-family:Microsoft YaHei;font-size:14px;">public class LockedUserMonitor implements Job {
-
static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);
-
static final Long LOCKED_USER_LAST_TIME = 1000 * 60 * 60L;
-
@SuppressWarnings("unchecked")
-
@Override
-
public void execute(JobExecutionContext context) throws JobExecutionException {
-
ServletContext sevletContext = null;
-
//job data
-
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
-
//提取servlet context 对象
-
sevletContext = (ServletContext)jobDataMap.get("servletContext");
-
Object lockedUsers = sevletContext.getAttribute(AgencyConstant.AGENCY_LOGIN_FAILED_USER_MAP);
-
System.out.println("lockedUsers : "+ lockedUsers);
-
if( lockedUsers != null ) {
-
Map<String, Long> lockedUserMap = (HashMap<String,Long>)lockedUsers;
-
checkLockedUsersStatus( lockedUserMap );
-
}
-
}
-
/**
-
* 将超时的用户接触锁定
-
* @param lockedUserMap
-
*/
-
private void checkLockedUsersStatus( Map<String,Long> lockedUserMap ) {
-
Set<Entry<String, Long>> userEntry = lockedUserMap.entrySet();
-
Iterator<Entry<String, Long>> userIt = userEntry.iterator();
-
Set<String> removeUsers = new HashSet<String>();
-
while( userIt.hasNext() ) {
-
Entry<String, Long> item = userIt.next();
-
String username = item.getKey();
-
Long lockedTime = item.getValue();
-
Long difference = lockedTime - System.currentTimeMillis();
-
if( difference >= LOCKED_USER_LAST_TIME ) {
-
removeUsers.add( username );
-
logger.info("当地时间:" + new Date(System.currentTimeMillis()) + ",用户 [" + username +"]接触登录锁定");
-
}
-
}
-
//移除已经锁定超过1小时的用户
-
Iterator<String> it = removeUsers.iterator();
-
while( it.hasNext() ) {
-
lockedUserMap.remove( it.next() );
-
}
-
}
-
}</span>
在Job中,通过JobDetail的JobDataMap获取到之前添加进去的Servlet Context对象引用,这样就可以操作上下文了。
通过配置文件配置的任务调度器,很方便。但是我这个地方需要在任务中访问到Servlet上下文,对整个应用进行控制,想到的解决方案如上所示。
在此记录,仅供学习,大家多多交流,共同进步~