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  /**
41   * An implementation of the {@link org.apache.shiro.session.mgt.SessionValidationScheduler SessionValidationScheduler} that uses Quartz to schedule a
42   * job to call {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()} on
43   * a regular basis.
44   *
45   * @since 0.1
46   */
47  public class QuartzSessionValidationScheduler implements SessionValidationScheduler {
48  
49      //TODO - complete JavaDoc
50  
51      /*--------------------------------------------
52      |             C O N S T A N T S             |
53      ============================================*/
54      /**
55       * The default interval at which sessions will be validated (1 hour);
56       * This can be overridden by calling {@link #setSessionValidationInterval(long)}
57       */
58      public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
59  
60      /**
61       * The name assigned to the quartz job.
62       */
63      private static final String JOB_NAME = "SessionValidationJob";
64  
65      /*--------------------------------------------
66      |    I N S T A N C E   V A R I A B L E S    |
67      ============================================*/
68      private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class);
69  
70      /**
71       * The configured Quartz scheduler to use to schedule the Quartz job.  If no scheduler is
72       * configured, the scheduler will be retrieved by calling {@link StdSchedulerFactory#getDefaultScheduler()}
73       */
74      private Scheduler scheduler;
75  
76      private boolean schedulerImplicitlyCreated = false;
77  
78      private boolean enabled = false;
79  
80      /**
81       * The session manager used to validate sessions.
82       */
83      private ValidatingSessionManager sessionManager;
84  
85      /**
86       * The session validation interval in milliseconds.
87       */
88      private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
89  
90      /*--------------------------------------------
91      |         C O N S T R U C T O R S           |
92      ============================================*/
93  
94      /**
95       * Default constructor.
96       */
97      public QuartzSessionValidationScheduler() {
98      }
99  
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 }