您的位置:首页 > 其它

Quartz任务调度,访问Servlet Context容器中的数据

2014-08-07 18:55 246 查看
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上下文,对整个应用进行控制,想到的解决方案如上所示。

在此记录,仅供学习,大家多多交流,共同进步~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息