View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.session.mgt.quartz;
20  
21  import org.quartz.JobBuilder;
22  import org.quartz.JobDetail;
23  import org.quartz.Scheduler;
24  import org.quartz.SchedulerException;
25  import org.quartz.SimpleTrigger;
26  import org.quartz.TriggerBuilder;
27  import org.quartz.TriggerKey;
28  import org.quartz.impl.StdSchedulerFactory;
29  
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import org.apache.shiro.session.mgt.DefaultSessionManager;
34  import org.apache.shiro.session.mgt.SessionValidationScheduler;
35  import org.apache.shiro.session.mgt.ValidatingSessionManager;
36  
37  import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
38  
39  /**
40   * An implementation of the {@link org.apache.shiro.session.mgt.SessionValidationScheduler SessionValidationScheduler}
41   * that uses Quartz to schedule a job to call {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()} on
42   * a regular basis.
43   *
44   * @since 0.1
45   */
46  public class QuartzSessionValidationScheduler implements SessionValidationScheduler {
47  
48      //TODO - complete JavaDoc
49  
50      /*--------------------------------------------
51      |             C O N S T A N T S             |
52      ============================================*/
53      /**
54       * The default interval at which sessions will be validated (1 hour);
55       * This can be overridden by calling {@link #setSessionValidationInterval(long)}
56       */
57      public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
58  
59      /**
60       * The name assigned to the quartz job.
61       */
62      private static final String JOB_NAME = "SessionValidationJob";
63  
64      /*--------------------------------------------
65      |    I N S T A N C E   V A R I A B L E S    |
66      ============================================*/
67      private static final Logger LOGGER = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class);
68  
69      /**
70       * The configured Quartz scheduler to use to schedule the Quartz job.  If no scheduler is
71       * configured, the scheduler will be retrieved by calling {@link StdSchedulerFactory#getDefaultScheduler()}
72       */
73      private Scheduler scheduler;
74  
75      private boolean schedulerImplicitlyCreated;
76  
77      private boolean enabled;
78  
79      /**
80       * The session manager used to validate sessions.
81       */
82      private ValidatingSessionManager sessionManager;
83  
84      /**
85       * The session validation interval in milliseconds.
86       */
87      private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
88  
89      /*--------------------------------------------
90      |         C O N S T R U C T O R S           |
91      ============================================*/
92  
93      /**
94       * Default constructor.
95       */
96      public QuartzSessionValidationScheduler() {
97      }
98  
99      /**
100      * Constructor that specifies the session manager that should be used for validating sessions.
101      *
102      * @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions.
103      */
104     public QuartzSessionValidationScheduler(ValidatingSessionManager sessionManager) {
105         this.sessionManager = sessionManager;
106     }
107 
108     /*--------------------------------------------
109     |  A C C E S S O R S / M O D I F I E R S    |
110     ============================================*/
111 
112     protected Scheduler getScheduler() throws SchedulerException {
113         if (scheduler == null) {
114             scheduler = StdSchedulerFactory.getDefaultScheduler();
115             schedulerImplicitlyCreated = true;
116         }
117         return scheduler;
118     }
119 
120     public void setScheduler(Scheduler scheduler) {
121         this.scheduler = scheduler;
122     }
123 
124     public void setSessionManager(ValidatingSessionManager sessionManager) {
125         this.sessionManager = sessionManager;
126     }
127 
128     public boolean isEnabled() {
129         return this.enabled;
130     }
131 
132     /**
133      * Specifies how frequently (in milliseconds) this Scheduler will call the
134      * {@link ValidatingSessionManager#validateSessions() ValidatingSessionManager#validateSessions()} method.
135      *
136      * <p>Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
137      *
138      * @param sessionValidationInterval
139      */
140     public void setSessionValidationInterval(long sessionValidationInterval) {
141         this.sessionValidationInterval = sessionValidationInterval;
142     }
143 
144     /*--------------------------------------------
145     |               M E T H O D S               |
146     ============================================*/
147 
148     /**
149      * Starts session validation by creating a Quartz simple trigger, linking it to
150      * the {@link QuartzSessionValidationJob}, and scheduling it with the Quartz scheduler.
151      */
152     public void enableSessionValidation() {
153 
154         if (LOGGER.isDebugEnabled()) {
155             LOGGER.debug("Scheduling session validation job using Quartz with "
156                     + "session validation interval of [" + sessionValidationInterval + "]ms...");
157         }
158 
159         try {
160             TriggerBuilder<SimpleTrigger> triggerBuilder =
161                     TriggerBuilder.newTrigger()
162                             .withIdentity(getClass().getName(), Scheduler.DEFAULT_GROUP)
163                             .withSchedule(simpleSchedule()
164                                     .withIntervalInMilliseconds(sessionValidationInterval)
165                                     .withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY));
166             SimpleTrigger trigger = triggerBuilder.build();
167 
168             JobDetail detail = JobBuilder.newJob(QuartzSessionValidationJob.class)
169                     .withIdentity(JOB_NAME, Scheduler.DEFAULT_GROUP).build();
170             detail.getJobDataMap().put(QuartzSessionValidationJob.SESSION_MANAGER_KEY, sessionManager);
171 
172             Scheduler scheduler = getScheduler();
173 
174             scheduler.scheduleJob(detail, trigger);
175             if (schedulerImplicitlyCreated) {
176                 scheduler.start();
177                 if (LOGGER.isDebugEnabled()) {
178                     LOGGER.debug("Successfully started implicitly created Quartz Scheduler instance.");
179                 }
180             }
181             this.enabled = true;
182 
183             if (LOGGER.isDebugEnabled()) {
184                 LOGGER.debug("Session validation job successfully scheduled with Quartz.");
185             }
186 
187         } catch (SchedulerException e) {
188             if (LOGGER.isErrorEnabled()) {
189                 LOGGER.error("Error starting the Quartz session validation job.  Session validation may not occur.", e);
190             }
191         }
192     }
193 
194     @SuppressWarnings("checkstyle:NPathComplexity")
195     public void disableSessionValidation() {
196         if (LOGGER.isDebugEnabled()) {
197             LOGGER.debug("Stopping Quartz session validation job...");
198         }
199 
200         Scheduler scheduler;
201         try {
202             scheduler = getScheduler();
203             if (scheduler == null) {
204                 if (LOGGER.isWarnEnabled()) {
205                     LOGGER.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 (LOGGER.isWarnEnabled()) {
213                 LOGGER.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 (LOGGER.isDebugEnabled()) {
221                 LOGGER.debug("Quartz session validation job stopped successfully.");
222             }
223         } catch (SchedulerException e) {
224             if (LOGGER.isDebugEnabled()) {
225                 LOGGER.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 (LOGGER.isWarnEnabled()) {
237                     LOGGER.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 }