Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CachingSessionDAO |
|
| 1.9047619047619047;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 | } |