Coverage Report - org.apache.shiro.session.mgt.AbstractValidatingSessionManager
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractValidatingSessionManager
79%
94/118
57%
24/42
2.074
 
 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;
 20  
 
 21  
 import org.apache.shiro.authz.AuthorizationException;
 22  
 import org.apache.shiro.session.ExpiredSessionException;
 23  
 import org.apache.shiro.session.InvalidSessionException;
 24  
 import org.apache.shiro.session.Session;
 25  
 import org.apache.shiro.session.UnknownSessionException;
 26  
 import org.apache.shiro.util.Destroyable;
 27  
 import org.apache.shiro.util.LifecycleUtils;
 28  
 import org.slf4j.Logger;
 29  
 import org.slf4j.LoggerFactory;
 30  
 
 31  
 import java.util.Collection;
 32  
 
 33  
 
 34  
 /**
 35  
  * Default business-tier implementation of the {@link ValidatingSessionManager} interface.
 36  
  *
 37  
  * @since 0.1
 38  
  */
 39  
 public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager
 40  
         implements ValidatingSessionManager, Destroyable {
 41  
 
 42  
     //TODO - complete JavaDoc
 43  
 
 44  2
     private static final Logger log = LoggerFactory.getLogger(AbstractValidatingSessionManager.class);
 45  
 
 46  
     /**
 47  
      * The default interval at which sessions will be validated (1 hour);
 48  
      * This can be overridden by calling {@link #setSessionValidationInterval(long)}
 49  
      */
 50  
     public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_HOUR;
 51  
 
 52  
     protected boolean sessionValidationSchedulerEnabled;
 53  
 
 54  
     /**
 55  
      * Scheduler used to validate sessions on a regular basis.
 56  
      */
 57  
     protected SessionValidationScheduler sessionValidationScheduler;
 58  
 
 59  
     protected long sessionValidationInterval;
 60  
 
 61  90
     public AbstractValidatingSessionManager() {
 62  90
         this.sessionValidationSchedulerEnabled = true;
 63  90
         this.sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
 64  90
     }
 65  
 
 66  
     public boolean isSessionValidationSchedulerEnabled() {
 67  462
         return sessionValidationSchedulerEnabled;
 68  
     }
 69  
 
 70  
     @SuppressWarnings({"UnusedDeclaration"})
 71  
     public void setSessionValidationSchedulerEnabled(boolean sessionValidationSchedulerEnabled) {
 72  22
         this.sessionValidationSchedulerEnabled = sessionValidationSchedulerEnabled;
 73  22
     }
 74  
 
 75  
     public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) {
 76  64
         this.sessionValidationScheduler = sessionValidationScheduler;
 77  64
     }
 78  
 
 79  
     public SessionValidationScheduler getSessionValidationScheduler() {
 80  550
         return sessionValidationScheduler;
 81  
     }
 82  
 
 83  
     private void enableSessionValidationIfNecessary() {
 84  462
         SessionValidationScheduler scheduler = getSessionValidationScheduler();
 85  462
         if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
 86  36
             enableSessionValidation();
 87  
         }
 88  462
     }
 89  
 
 90  
     /**
 91  
      * If using the underlying default <tt>SessionValidationScheduler</tt> (that is, the
 92  
      * {@link #setSessionValidationScheduler(SessionValidationScheduler) setSessionValidationScheduler} method is
 93  
      * never called) , this method allows one to specify how
 94  
      * frequently session should be validated (to check for orphans).  The default value is
 95  
      * {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
 96  
      * <p/>
 97  
      * If you override the default scheduler, it is assumed that overriding instance 'knows' how often to
 98  
      * validate sessions, and this attribute will be ignored.
 99  
      * <p/>
 100  
      * Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
 101  
      *
 102  
      * @param sessionValidationInterval the time in milliseconds between checking for valid sessions to reap orphans.
 103  
      */
 104  
     public void setSessionValidationInterval(long sessionValidationInterval) {
 105  0
         this.sessionValidationInterval = sessionValidationInterval;
 106  0
     }
 107  
 
 108  
     public long getSessionValidationInterval() {
 109  36
         return sessionValidationInterval;
 110  
     }
 111  
 
 112  
     @Override
 113  
     protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
 114  406
         enableSessionValidationIfNecessary();
 115  
 
 116  406
         log.trace("Attempting to retrieve session with key {}", key);
 117  
 
 118  406
         Session s = retrieveSession(key);
 119  406
         if (s != null) {
 120  406
             validate(s, key);
 121  
         }
 122  398
         return s;
 123  
     }
 124  
 
 125  
     /**
 126  
      * Looks up a session from the underlying data store based on the specified session key.
 127  
      *
 128  
      * @param key the session key to use to look up the target session.
 129  
      * @return the session identified by {@code sessionId}.
 130  
      * @throws UnknownSessionException if there is no session identified by {@code sessionId}.
 131  
      */
 132  
     protected abstract Session retrieveSession(SessionKey key) throws UnknownSessionException;
 133  
 
 134  
     protected Session createSession(SessionContext context) throws AuthorizationException {
 135  56
         enableSessionValidationIfNecessary();
 136  56
         return doCreateSession(context);
 137  
     }
 138  
 
 139  
     protected abstract Session doCreateSession(SessionContext initData) throws AuthorizationException;
 140  
 
 141  
     protected void validate(Session session, SessionKey key) throws InvalidSessionException {
 142  
         try {
 143  412
             doValidate(session);
 144  12
         } catch (ExpiredSessionException ese) {
 145  12
             onExpiration(session, ese, key);
 146  10
             throw ese;
 147  0
         } catch (InvalidSessionException ise) {
 148  0
             onInvalidation(session, ise, key);
 149  0
             throw ise;
 150  400
         }
 151  400
     }
 152  
 
 153  
     protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
 154  12
         log.trace("Session with id [{}] has expired.", s.getId());
 155  
         try {
 156  12
             onExpiration(s);
 157  12
             notifyExpiration(s);
 158  
         } finally {
 159  12
             afterExpired(s);
 160  10
         }
 161  10
     }
 162  
 
 163  
     protected void onExpiration(Session session) {
 164  2
         onChange(session);
 165  2
     }
 166  
 
 167  
     protected void afterExpired(Session session) {
 168  2
     }
 169  
 
 170  
     protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey key) {
 171  0
         if (ise instanceof ExpiredSessionException) {
 172  0
             onExpiration(s, (ExpiredSessionException) ise, key);
 173  0
             return;
 174  
         }
 175  0
         log.trace("Session with id [{}] is invalid.", s.getId());
 176  
         try {
 177  0
             onStop(s);
 178  0
             notifyStop(s);
 179  
         } finally {
 180  0
             afterStopped(s);
 181  0
         }
 182  0
     }
 183  
 
 184  
     protected void doValidate(Session session) throws InvalidSessionException {
 185  412
         if (session instanceof ValidatingSession) {
 186  412
             ((ValidatingSession) session).validate();
 187  
         } else {
 188  0
             String msg = "The " + getClass().getName() + " implementation only supports validating " +
 189  0
                     "Session implementations of the " + ValidatingSession.class.getName() + " interface.  " +
 190  
                     "Please either implement this interface in your session implementation or override the " +
 191  0
                     AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation.";
 192  0
             throw new IllegalStateException(msg);
 193  
         }
 194  400
     }
 195  
 
 196  
     /**
 197  
      * Subclass template hook in case per-session timeout is not based on
 198  
      * {@link org.apache.shiro.session.Session#getTimeout()}.
 199  
      * <p/>
 200  
      * <p>This implementation merely returns {@link org.apache.shiro.session.Session#getTimeout()}</p>
 201  
      *
 202  
      * @param session the session for which to determine session timeout.
 203  
      * @return the time in milliseconds the specified session may remain idle before expiring.
 204  
      */
 205  
     protected long getTimeout(Session session) {
 206  0
         return session.getTimeout();
 207  
     }
 208  
 
 209  
     protected SessionValidationScheduler createSessionValidationScheduler() {
 210  
         ExecutorServiceSessionValidationScheduler scheduler;
 211  
 
 212  36
         if (log.isDebugEnabled()) {
 213  36
             log.debug("No sessionValidationScheduler set.  Attempting to create default instance.");
 214  
         }
 215  36
         scheduler = new ExecutorServiceSessionValidationScheduler(this);
 216  36
         scheduler.setInterval(getSessionValidationInterval());
 217  36
         if (log.isTraceEnabled()) {
 218  36
             log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
 219  
         }
 220  36
         return scheduler;
 221  
     }
 222  
 
 223  
     protected synchronized void enableSessionValidation() {
 224  36
         SessionValidationScheduler scheduler = getSessionValidationScheduler();
 225  36
         if (scheduler == null) {
 226  36
             scheduler = createSessionValidationScheduler();
 227  36
             setSessionValidationScheduler(scheduler);
 228  36
             if (log.isInfoEnabled()) {
 229  36
                 log.info("Enabling session validation scheduler...");
 230  
             }
 231  36
             scheduler.enableSessionValidation();
 232  36
             afterSessionValidationEnabled();
 233  
         }
 234  36
     }
 235  
 
 236  
     protected void afterSessionValidationEnabled() {
 237  36
     }
 238  
 
 239  
     protected void disableSessionValidation() {
 240  52
         beforeSessionValidationDisabled();
 241  52
         SessionValidationScheduler scheduler = getSessionValidationScheduler();
 242  52
         if (scheduler != null) {
 243  
             try {
 244  28
                 scheduler.disableSessionValidation();
 245  28
                 if (log.isInfoEnabled()) {
 246  28
                     log.info("Disabled session validation scheduler.");
 247  
                 }
 248  0
             } catch (Exception e) {
 249  0
                 if (log.isDebugEnabled()) {
 250  0
                     String msg = "Unable to disable SessionValidationScheduler.  Ignoring (shutting down)...";
 251  0
                     log.debug(msg, e);
 252  
                 }
 253  28
             }
 254  28
             LifecycleUtils.destroy(scheduler);
 255  28
             setSessionValidationScheduler(null);
 256  
         }
 257  52
     }
 258  
 
 259  
     protected void beforeSessionValidationDisabled() {
 260  52
     }
 261  
 
 262  
     public void destroy() {
 263  52
         disableSessionValidation();
 264  52
     }
 265  
 
 266  
     /**
 267  
      * @see ValidatingSessionManager#validateSessions()
 268  
      */
 269  
     public void validateSessions() {
 270  4
         if (log.isInfoEnabled()) {
 271  4
             log.info("Validating all active sessions...");
 272  
         }
 273  
 
 274  4
         int invalidCount = 0;
 275  
 
 276  4
         Collection<Session> activeSessions = getActiveSessions();
 277  
 
 278  4
         if (activeSessions != null && !activeSessions.isEmpty()) {
 279  4
             for (Session s : activeSessions) {
 280  
                 try {
 281  
                     //simulate a lookup key to satisfy the method signature.
 282  
                     //this could probably stand to be cleaned up in future versions:
 283  6
                     SessionKey key = new DefaultSessionKey(s.getId());
 284  6
                     validate(s, key);
 285  4
                 } catch (InvalidSessionException e) {
 286  4
                     if (log.isDebugEnabled()) {
 287  4
                         boolean expired = (e instanceof ExpiredSessionException);
 288  4
                         String msg = "Invalidated session with id [" + s.getId() + "]" +
 289  
                                 (expired ? " (expired)" : " (stopped)");
 290  4
                         log.debug(msg);
 291  
                     }
 292  4
                     invalidCount++;
 293  2
                 }
 294  6
             }
 295  
         }
 296  
 
 297  4
         if (log.isInfoEnabled()) {
 298  4
             String msg = "Finished session validation.";
 299  4
             if (invalidCount > 0) {
 300  4
                 msg += "  [" + invalidCount + "] sessions were stopped.";
 301  
             } else {
 302  0
                 msg += "  No sessions were stopped.";
 303  
             }
 304  4
             log.info(msg);
 305  
         }
 306  4
     }
 307  
 
 308  
     protected abstract Collection<Session> getActiveSessions();
 309  
 }