1. 程式人生 > >Quartz任務排程,訪問Servlet Context容器中的資料

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