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

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监听器类,具体如下:

 
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">public class MyQuartzContextListener extends QuartzInitializerListener {

  2.  
  3. static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);

  4.  
  5. @Override

  6. public void contextInitialized(ServletContextEvent sce) {

  7.  
  8. super.contextInitialized(sce);

  9. ServletContext servletContext = sce.getServletContext();

  10. //scheduler factory

  11. StdSchedulerFactory sFactory = (StdSchedulerFactory)servletContext.getAttribute(QUARTZ_FACTORY_KEY);

  12. Scheduler scheduler = null;

  13. try {

  14. scheduler = sFactory.getScheduler();

  15. //定义一个JobDetail

  16. JobDetail jobDetail = new JobDetail("lockedUserJobDetail", "lockedUserGroup", LockedUserMonitor.class);

  17. // 将ServletContext对象放到map中,然后从job中取出来,从而取得路径

  18. Map<String, Object> map = new HashMap<String, Object>();

  19. map.put("servletContext", servletContext);

  20. //将servlet上下文添加到JobDataMap中

  21. JobDataMap dateMap = new JobDataMap(map);

  22. jobDetail.setJobDataMap(dateMap);

  23. //触发器

  24. Trigger trigger = new CronTrigger("lockedUserCronTrigger", "lockedUserCronTrigger", "0 0/1 * ? * *");

  25. //关联任务和触发器

  26. scheduler.scheduleJob(jobDetail, trigger);

  27. //开启调度

  28. scheduler.start();

  29. } catch (SchedulerException e) {

  30. logger.error("调度器 MyQuartzContextListener", e);

  31. e.printStackTrace();

  32. } catch (ParseException e) {

  33. logger.error("调度器 CronTrigger表达式解析错误", e);

  34. e.printStackTrace();

  35. }

  36. }

  37. }</span>


在web.xml配置文件添加

 
  1. <span style="font-family:Microsoft YaHei;font-size:14px;"><!-- 任务调度监听器 -->

  2. <listener>

  3. <listener-class>com.marketing.listener.MyQuartzContextListener</listener-class>

  4. </listener></span>

然后是任务具体实现类,实现org.quartz.job.Job接口

 
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">public class LockedUserMonitor implements Job {

  2. static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);

  3. static final Long LOCKED_USER_LAST_TIME = 1000 * 60 * 60L;

  4.  
  5. @SuppressWarnings("unchecked")

  6. @Override

  7. public void execute(JobExecutionContext context) throws JobExecutionException {

  8.  
  9. ServletContext sevletContext = null;

  10. //job data

  11. JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

  12. //提取servlet context 对象

  13. sevletContext = (ServletContext)jobDataMap.get("servletContext");

  14. Object lockedUsers = sevletContext.getAttribute(AgencyConstant.AGENCY_LOGIN_FAILED_USER_MAP);

  15. System.out.println("lockedUsers : "+ lockedUsers);

  16. if( lockedUsers != null ) {

  17. Map<String, Long> lockedUserMap = (HashMap<String,Long>)lockedUsers;

  18. checkLockedUsersStatus( lockedUserMap );

  19. }

  20.  
  21. }

  22.  
  23. /**

  24. * 将超时的用户接触锁定

  25. * @param lockedUserMap

  26. */

  27. private void checkLockedUsersStatus( Map<String,Long> lockedUserMap ) {

  28. Set<Entry<String, Long>> userEntry = lockedUserMap.entrySet();

  29. Iterator<Entry<String, Long>> userIt = userEntry.iterator();

  30. Set<String> removeUsers = new HashSet<String>();

  31. while( userIt.hasNext() ) {

  32. Entry<String, Long> item = userIt.next();

  33. String username = item.getKey();

  34. Long lockedTime = item.getValue();

  35. Long difference = lockedTime - System.currentTimeMillis();

  36. if( difference >= LOCKED_USER_LAST_TIME ) {

  37. removeUsers.add( username );

  38. logger.info("当地时间:" + new Date(System.currentTimeMillis()) + ",用户 [" + username +"]接触登录锁定");

  39. }

  40. }

  41. //移除已经锁定超过1小时的用户

  42. Iterator<String> it = removeUsers.iterator();

  43. while( it.hasNext() ) {

  44. lockedUserMap.remove( it.next() );

  45. }

  46. }

  47. }</span>

在Job中,通过JobDetail的JobDataMap获取到之前添加进去的Servlet Context对象引用,这样就可以操作上下文了。

通过配置文件配置的任务调度器,很方便。但是我这个地方需要在任务中访问到Servlet上下文,对整个应用进行控制,想到的解决方案如上所示。

在此记录,仅供学习,大家多多交流,共同进步~

文章标签: quartzservlet任务调度quartz和Servlet交互

https://blog.csdn.net/chenjing502/article/details/38423251

猜你喜欢

转载自blog.csdn.net/xuheng8600/article/details/81543229