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