Coverage Report - org.apache.shiro.session.mgt.eis.CachingSessionDAO
 
Classes in this File Line Coverage Branch Coverage Complexity
CachingSessionDAO
62%
42/67
39%
11/28
1.905
 
 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.eis;
 20  
 
 21  
 import org.apache.shiro.cache.Cache;
 22  
 import org.apache.shiro.cache.CacheManager;
 23  
 import org.apache.shiro.cache.CacheManagerAware;
 24  
 import org.apache.shiro.session.Session;
 25  
 import org.apache.shiro.session.UnknownSessionException;
 26  
 import org.apache.shiro.session.mgt.ValidatingSession;
 27  
 
 28  
 import java.io.Serializable;
 29  
 import java.util.Collection;
 30  
 import java.util.Collections;
 31  
 
 32  
 /**
 33  
  * An CachingSessionDAO is a SessionDAO that provides a transparent caching layer between the components that
 34  
  * use it and the underlying EIS (Enterprise Information System) session backing store (for example, filesystem,
 35  
  * database, enterprise grid/cloud, etc).
 36  
  * <p/>
 37  
  * This implementation caches all active sessions in a configured
 38  
  * {@link #getActiveSessionsCache() activeSessionsCache}.  This property is {@code null} by default and if one is
 39  
  * not explicitly set, a {@link #setCacheManager cacheManager} is expected to be configured which will in turn be used
 40  
  * to acquire the {@code Cache} instance to use for the {@code activeSessionsCache}.
 41  
  * <p/>
 42  
  * All {@code SessionDAO} methods are implemented by this class to employ
 43  
  * caching behavior and delegates the actual EIS operations to respective do* methods to be implemented by
 44  
  * subclasses (doCreate, doRead, etc).
 45  
  *
 46  
  * @since 0.2
 47  
  */
 48  
 public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {
 49  
 
 50  
     /**
 51  
      * The default active sessions cache name, equal to {@code shiro-activeSessionCache}.
 52  
      */
 53  
     public static final String ACTIVE_SESSION_CACHE_NAME = "shiro-activeSessionCache";
 54  
 
 55  
     /**
 56  
      * The CacheManager to use to acquire the Session cache.
 57  
      */
 58  
     private CacheManager cacheManager;
 59  
 
 60  
     /**
 61  
      * The Cache instance responsible for caching Sessions.
 62  
      */
 63  
     private Cache<Serializable, Session> activeSessions;
 64  
 
 65  
     /**
 66  
      * The name of the session cache, defaults to {@link #ACTIVE_SESSION_CACHE_NAME}.
 67  
      */
 68  1
     private String activeSessionsCacheName = ACTIVE_SESSION_CACHE_NAME;
 69  
 
 70  
     /**
 71  
      * Default no-arg constructor.
 72  
      */
 73  1
     public CachingSessionDAO() {
 74  1
     }
 75  
 
 76  
     /**
 77  
      * Sets the cacheManager to use for acquiring the {@link #getActiveSessionsCache() activeSessionsCache} if
 78  
      * one is not configured.
 79  
      *
 80  
      * @param cacheManager the manager to use for constructing the session cache.
 81  
      */
 82  
     public void setCacheManager(CacheManager cacheManager) {
 83  2
         this.cacheManager = cacheManager;
 84  2
     }
 85  
 
 86  
     /**
 87  
      * Returns the CacheManager to use for acquiring the {@link #getActiveSessionsCache() activeSessionsCache} if
 88  
      * one is not configured.  That is, the {@code CacheManager} will only be used if the
 89  
      * {@link #getActiveSessionsCache() activeSessionsCache} property is {@code null}.
 90  
      *
 91  
      * @return the CacheManager used by the implementation that creates the activeSessions Cache.
 92  
      */
 93  
     public CacheManager getCacheManager() {
 94  1
         return cacheManager;
 95  
     }
 96  
 
 97  
     /**
 98  
      * Returns the name of the actives sessions cache to be returned by the {@code CacheManager}.  Unless
 99  
      * overridden by {@link #setActiveSessionsCacheName(String)}, defaults to {@link #ACTIVE_SESSION_CACHE_NAME}.
 100  
      *
 101  
      * @return the name of the active sessions cache.
 102  
      */
 103  
     public String getActiveSessionsCacheName() {
 104  1
         return activeSessionsCacheName;
 105  
     }
 106  
 
 107  
     /**
 108  
      * Sets the name of the active sessions cache to be returned by the {@code CacheManager}.  Defaults to
 109  
      * {@link #ACTIVE_SESSION_CACHE_NAME}.
 110  
      *
 111  
      * @param activeSessionsCacheName the name of the active sessions cache to be returned by the {@code CacheManager}.
 112  
      */
 113  
     public void setActiveSessionsCacheName(String activeSessionsCacheName) {
 114  0
         this.activeSessionsCacheName = activeSessionsCacheName;
 115  0
     }
 116  
 
 117  
     /**
 118  
      * Returns the cache instance to use for storing active sessions.  If one is not available (it is {@code null}),
 119  
      * it will be {@link CacheManager#getCache(String) acquired} from the {@link #setCacheManager configured}
 120  
      * {@code CacheManager} using the {@link #getActiveSessionsCacheName() activeSessionsCacheName}.
 121  
      *
 122  
      * @return the cache instance to use for storing active sessions or {@code null} if the {@code Cache} instance
 123  
      *         should be retrieved from the
 124  
      */
 125  
     public Cache<Serializable, Session> getActiveSessionsCache() {
 126  1
         return this.activeSessions;
 127  
     }
 128  
 
 129  
     /**
 130  
      * Sets the cache instance to use for storing active sessions.  If one is not set (it remains {@code null}),
 131  
      * it will be {@link CacheManager#getCache(String) acquired} from the {@link #setCacheManager configured}
 132  
      * {@code CacheManager} using the {@link #getActiveSessionsCacheName() activeSessionsCacheName}.
 133  
      *
 134  
      * @param cache the cache instance to use for storing active sessions or {@code null} if the cache is to be
 135  
      *              acquired from the {@link #setCacheManager configured} {@code CacheManager}.
 136  
      */
 137  
     public void setActiveSessionsCache(Cache<Serializable, Session> cache) {
 138  0
         this.activeSessions = cache;
 139  0
     }
 140  
 
 141  
     /**
 142  
      * Returns the active sessions cache, but if that cache instance is null, first lazily creates the cache instance
 143  
      * via the {@link #createActiveSessionsCache()} method and then returns the instance.
 144  
      * <p/>
 145  
      * Note that this method will only return a non-null value code if the {@code CacheManager} has been set.  If
 146  
      * not set, there will be no cache.
 147  
      *
 148  
      * @return the active sessions cache instance.
 149  
      */
 150  
     private Cache<Serializable, Session> getActiveSessionsCacheLazy() {
 151  9
         if (this.activeSessions == null) {
 152  1
             this.activeSessions = createActiveSessionsCache();
 153  
         }
 154  9
         return activeSessions;
 155  
     }
 156  
 
 157  
     /**
 158  
      * Creates a cache instance used to store active sessions.  Creation is done by first
 159  
      * {@link #getCacheManager() acquiring} the {@code CacheManager}.  If the cache manager is not null, the
 160  
      * cache returned is that resulting from the following call:
 161  
      * <pre>       String name = {@link #getActiveSessionsCacheName() getActiveSessionsCacheName()};
 162  
      * cacheManager.getCache(name);</pre>
 163  
      *
 164  
      * @return a cache instance used to store active sessions, or {@code null} if the {@code CacheManager} has
 165  
      *         not been set.
 166  
      */
 167  
     protected Cache<Serializable, Session> createActiveSessionsCache() {
 168  1
         Cache<Serializable, Session> cache = null;
 169  1
         CacheManager mgr = getCacheManager();
 170  1
         if (mgr != null) {
 171  1
             String name = getActiveSessionsCacheName();
 172  1
             cache = mgr.getCache(name);
 173  
         }
 174  1
         return cache;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Calls {@code super.create(session)}, then caches the session keyed by the returned {@code sessionId}, and then
 179  
      * returns this {@code sessionId}.
 180  
      *
 181  
      * @param session Session object to create in the EIS and then cache.
 182  
      */
 183  
     public Serializable create(Session session) {
 184  1
         Serializable sessionId = super.create(session);
 185  1
         cache(session, sessionId);
 186  1
         return sessionId;
 187  
     }
 188  
 
 189  
     /**
 190  
      * Returns the cached session with the corresponding {@code sessionId} or {@code null} if there is
 191  
      * no session cached under that id (or if there is no Cache).
 192  
      *
 193  
      * @param sessionId the id of the cached session to acquire.
 194  
      * @return the cached session with the corresponding {@code sessionId}, or {@code null} if the session
 195  
      *         does not exist or is not cached.
 196  
      */
 197  
     protected Session getCachedSession(Serializable sessionId) {
 198  4
         Session cached = null;
 199  4
         if (sessionId != null) {
 200  4
             Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
 201  4
             if (cache != null) {
 202  4
                 cached = getCachedSession(sessionId, cache);
 203  
             }
 204  
         }
 205  4
         return cached;
 206  
     }
 207  
 
 208  
     /**
 209  
      * Returns the Session with the specified id from the specified cache.  This method simply calls
 210  
      * {@code cache.get(sessionId)} and can be overridden by subclasses for custom acquisition behavior.
 211  
      *
 212  
      * @param sessionId the id of the session to acquire.
 213  
      * @param cache     the cache to acquire the session from
 214  
      * @return the cached session, or {@code null} if the session wasn't in the cache.
 215  
      */
 216  
     protected Session getCachedSession(Serializable sessionId, Cache<Serializable, Session> cache) {
 217  4
         return cache.get(sessionId);
 218  
     }
 219  
 
 220  
     /**
 221  
      * Caches the specified session under the cache entry key of {@code sessionId}.
 222  
      *
 223  
      * @param session   the session to cache
 224  
      * @param sessionId the session id, to be used as the cache entry key.
 225  
      * @since 1.0
 226  
      */
 227  
     protected void cache(Session session, Serializable sessionId) {
 228  5
         if (session == null || sessionId == null) {
 229  0
             return;
 230  
         }
 231  5
         Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
 232  5
         if (cache == null) {
 233  0
             return;
 234  
         }
 235  5
         cache(session, sessionId, cache);
 236  5
     }
 237  
 
 238  
     /**
 239  
      * Caches the specified session in the given cache under the key of {@code sessionId}.  This implementation
 240  
      * simply calls {@code cache.put(sessionId,session)} and can be overridden for custom behavior.
 241  
      *
 242  
      * @param session   the session to cache
 243  
      * @param sessionId the id of the session, expected to be the cache key.
 244  
      * @param cache     the cache to store the session
 245  
      */
 246  
     protected void cache(Session session, Serializable sessionId, Cache<Serializable, Session> cache) {
 247  5
         cache.put(sessionId, session);
 248  5
     }
 249  
 
 250  
     /**
 251  
      * Attempts to acquire the Session from the cache first using the session ID as the cache key.  If no session
 252  
      * is found, {@code super.readSession(sessionId)} is called to perform the actual retrieval.
 253  
      *
 254  
      * @param sessionId the id of the session to retrieve from the EIS.
 255  
      * @return the session identified by {@code sessionId} in the EIS.
 256  
      * @throws UnknownSessionException if the id specified does not correspond to any session in the cache or EIS.
 257  
      */
 258  
     public Session readSession(Serializable sessionId) throws UnknownSessionException {
 259  4
         Session s = getCachedSession(sessionId);
 260  4
         if (s == null) {
 261  0
             s = super.readSession(sessionId);
 262  
         }
 263  4
         return s;
 264  
     }
 265  
 
 266  
     /**
 267  
      * Updates the state of the given session to the EIS by first delegating to
 268  
      * {@link #doUpdate(org.apache.shiro.session.Session)}.  If the session is a {@link ValidatingSession}, it will
 269  
      * be added to the cache only if it is {@link ValidatingSession#isValid()} and if invalid, will be removed from the
 270  
      * cache.  If it is not a {@code ValidatingSession} instance, it will be added to the cache in any event.
 271  
      *
 272  
      * @param session the session object to update in the EIS.
 273  
      * @throws UnknownSessionException if no existing EIS session record exists with the
 274  
      *                                 identifier of {@link Session#getId() session.getId()}
 275  
      */
 276  
     public void update(Session session) throws UnknownSessionException {
 277  4
         doUpdate(session);
 278  4
         if (session instanceof ValidatingSession) {
 279  4
             if (((ValidatingSession) session).isValid()) {
 280  4
                 cache(session, session.getId());
 281  
             } else {
 282  0
                 uncache(session);
 283  
             }
 284  
         } else {
 285  0
             cache(session, session.getId());
 286  
         }
 287  4
     }
 288  
 
 289  
     /**
 290  
      * Subclass implementation hook to actually persist the {@code Session}'s state to the underlying EIS.
 291  
      *
 292  
      * @param session the session object whose state will be propagated to the EIS.
 293  
      */
 294  
     protected abstract void doUpdate(Session session);
 295  
 
 296  
     /**
 297  
      * Removes the specified session from any cache and then permanently deletes the session from the EIS by
 298  
      * delegating to {@link #doDelete}.
 299  
      *
 300  
      * @param session the session to remove from caches and permanently delete from the EIS.
 301  
      */
 302  
     public void delete(Session session) {
 303  0
         uncache(session);
 304  0
         doDelete(session);
 305  0
     }
 306  
 
 307  
     /**
 308  
      * Subclass implementation hook to permanently delete the given Session from the underlying EIS.
 309  
      *
 310  
      * @param session the session instance to permanently delete from the EIS.
 311  
      */
 312  
     protected abstract void doDelete(Session session);
 313  
 
 314  
     /**
 315  
      * Removes the specified Session from the cache.
 316  
      *
 317  
      * @param session the session to remove from the cache.
 318  
      */
 319  
     protected void uncache(Session session) {
 320  0
         if (session == null) {
 321  0
             return;
 322  
         }
 323  0
         Serializable id = session.getId();
 324  0
         if (id == null) {
 325  0
             return;
 326  
         }
 327  0
         Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
 328  0
         if (cache != null) {
 329  0
             cache.remove(id);
 330  
         }
 331  0
     }
 332  
 
 333  
     /**
 334  
      * Returns all active sessions in the system.
 335  
      * <p/>
 336  
      * <p>This implementation merely returns the sessions found in the activeSessions cache.  Subclass implementations
 337  
      * may wish to override this method to retrieve them in a different way, perhaps by an RDBMS query or by other
 338  
      * means.
 339  
      *
 340  
      * @return the sessions found in the activeSessions cache.
 341  
      */
 342  
     public Collection<Session> getActiveSessions() {
 343  0
         Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
 344  0
         if (cache != null) {
 345  0
             return cache.values();
 346  
         } else {
 347  0
             return Collections.emptySet();
 348  
         }
 349  
     }
 350  
 }