001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.session.mgt.quartz; 020 021import org.quartz.JobBuilder; 022import org.quartz.JobDetail; 023import org.quartz.Scheduler; 024import org.quartz.SchedulerException; 025import org.quartz.SimpleTrigger; 026import org.quartz.TriggerBuilder; 027import org.quartz.TriggerKey; 028import org.quartz.impl.StdSchedulerFactory; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.shiro.session.mgt.DefaultSessionManager; 034import org.apache.shiro.session.mgt.SessionValidationScheduler; 035import org.apache.shiro.session.mgt.ValidatingSessionManager; 036 037import static org.quartz.SimpleScheduleBuilder.simpleSchedule; 038 039 040/** 041 * An implementation of the {@link org.apache.shiro.session.mgt.SessionValidationScheduler SessionValidationScheduler} that uses Quartz to schedule a 042 * job to call {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()} on 043 * a regular basis. 044 * 045 * @since 0.1 046 */ 047public class QuartzSessionValidationScheduler implements SessionValidationScheduler { 048 049 //TODO - complete JavaDoc 050 051 /*-------------------------------------------- 052 | C O N S T A N T S | 053 ============================================*/ 054 /** 055 * The default interval at which sessions will be validated (1 hour); 056 * This can be overridden by calling {@link #setSessionValidationInterval(long)} 057 */ 058 public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; 059 060 /** 061 * The name assigned to the quartz job. 062 */ 063 private static final String JOB_NAME = "SessionValidationJob"; 064 065 /*-------------------------------------------- 066 | I N S T A N C E V A R I A B L E S | 067 ============================================*/ 068 private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class); 069 070 /** 071 * The configured Quartz scheduler to use to schedule the Quartz job. If no scheduler is 072 * configured, the scheduler will be retrieved by calling {@link StdSchedulerFactory#getDefaultScheduler()} 073 */ 074 private Scheduler scheduler; 075 076 private boolean schedulerImplicitlyCreated = false; 077 078 private boolean enabled = false; 079 080 /** 081 * The session manager used to validate sessions. 082 */ 083 private ValidatingSessionManager sessionManager; 084 085 /** 086 * The session validation interval in milliseconds. 087 */ 088 private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL; 089 090 /*-------------------------------------------- 091 | C O N S T R U C T O R S | 092 ============================================*/ 093 094 /** 095 * Default constructor. 096 */ 097 public QuartzSessionValidationScheduler() { 098 } 099 100 /** 101 * Constructor that specifies the session manager that should be used for validating sessions. 102 * 103 * @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions. 104 */ 105 public QuartzSessionValidationScheduler(ValidatingSessionManager sessionManager) { 106 this.sessionManager = sessionManager; 107 } 108 109 /*-------------------------------------------- 110 | A C C E S S O R S / M O D I F I E R S | 111 ============================================*/ 112 113 protected Scheduler getScheduler() throws SchedulerException { 114 if (scheduler == null) { 115 scheduler = StdSchedulerFactory.getDefaultScheduler(); 116 schedulerImplicitlyCreated = true; 117 } 118 return scheduler; 119 } 120 121 public void setScheduler(Scheduler scheduler) { 122 this.scheduler = scheduler; 123 } 124 125 public void setSessionManager(ValidatingSessionManager sessionManager) { 126 this.sessionManager = sessionManager; 127 } 128 129 public boolean isEnabled() { 130 return this.enabled; 131 } 132 133 /** 134 * Specifies how frequently (in milliseconds) this Scheduler will call the 135 * {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions() ValidatingSessionManager#validateSessions()} method. 136 * 137 * <p>Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}. 138 * 139 * @param sessionValidationInterval 140 */ 141 public void setSessionValidationInterval(long sessionValidationInterval) { 142 this.sessionValidationInterval = sessionValidationInterval; 143 } 144 145 /*-------------------------------------------- 146 | M E T H O D S | 147 ============================================*/ 148 149 /** 150 * Starts session validation by creating a Quartz simple trigger, linking it to 151 * the {@link QuartzSessionValidationJob}, and scheduling it with the Quartz scheduler. 152 */ 153 public void enableSessionValidation() { 154 155 if (log.isDebugEnabled()) { 156 log.debug("Scheduling session validation job using Quartz with " + 157 "session validation interval of [" + sessionValidationInterval + "]ms..."); 158 } 159 160 try { 161 TriggerBuilder<SimpleTrigger> triggerBuilder = 162 TriggerBuilder.newTrigger() 163 .withIdentity(getClass().getName(), Scheduler.DEFAULT_GROUP) 164 .withSchedule(simpleSchedule() 165 .withIntervalInMilliseconds(sessionValidationInterval) 166 .withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY)); 167 SimpleTrigger trigger = triggerBuilder.build(); 168 169 JobDetail detail = JobBuilder.newJob(QuartzSessionValidationJob.class) 170 .withIdentity(JOB_NAME, Scheduler.DEFAULT_GROUP).build(); 171 detail.getJobDataMap().put(QuartzSessionValidationJob.SESSION_MANAGER_KEY, sessionManager); 172 173 Scheduler scheduler = getScheduler(); 174 175 scheduler.scheduleJob(detail, trigger); 176 if (schedulerImplicitlyCreated) { 177 scheduler.start(); 178 if (log.isDebugEnabled()) { 179 log.debug("Successfully started implicitly created Quartz Scheduler instance."); 180 } 181 } 182 this.enabled = true; 183 184 if (log.isDebugEnabled()) { 185 log.debug("Session validation job successfully scheduled with Quartz."); 186 } 187 188 } catch (SchedulerException e) { 189 if (log.isErrorEnabled()) { 190 log.error("Error starting the Quartz session validation job. Session validation may not occur.", e); 191 } 192 } 193 } 194 195 public void disableSessionValidation() { 196 if (log.isDebugEnabled()) { 197 log.debug("Stopping Quartz session validation job..."); 198 } 199 200 Scheduler scheduler; 201 try { 202 scheduler = getScheduler(); 203 if (scheduler == null) { 204 if (log.isWarnEnabled()) { 205 log.warn("getScheduler() method returned a null Quartz scheduler, which is unexpected. Please " + 206 "check your configuration and/or implementation. Returning quietly since there is no " + 207 "validation job to remove (scheduler does not exist)."); 208 } 209 return; 210 } 211 } catch (SchedulerException e) { 212 if (log.isWarnEnabled()) { 213 log.warn("Unable to acquire Quartz Scheduler. Ignoring and returning (already stopped?)", e); 214 } 215 return; 216 } 217 218 try { 219 scheduler.unscheduleJob(TriggerKey.triggerKey(JOB_NAME, Scheduler.DEFAULT_GROUP)); 220 if (log.isDebugEnabled()) { 221 log.debug("Quartz session validation job stopped successfully."); 222 } 223 } catch (SchedulerException e) { 224 if (log.isDebugEnabled()) { 225 log.debug("Could not cleanly remove SessionValidationJob from Quartz scheduler. " + 226 "Ignoring and stopping.", e); 227 } 228 } 229 230 this.enabled = false; 231 232 if (schedulerImplicitlyCreated) { 233 try { 234 scheduler.shutdown(); 235 } catch (SchedulerException e) { 236 if (log.isWarnEnabled()) { 237 log.warn("Unable to cleanly shutdown implicitly created Quartz Scheduler instance.", e); 238 } 239 } finally { 240 setScheduler(null); 241 schedulerImplicitlyCreated = false; 242 } 243 } 244 245 246 } 247}