Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultSessionManager |
|
| 1.6363636363636365;1.636 |
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.cache.CacheManager; | |
22 | import org.apache.shiro.cache.CacheManagerAware; | |
23 | import org.apache.shiro.session.Session; | |
24 | import org.apache.shiro.session.UnknownSessionException; | |
25 | import org.apache.shiro.session.mgt.eis.MemorySessionDAO; | |
26 | import org.apache.shiro.session.mgt.eis.SessionDAO; | |
27 | import org.slf4j.Logger; | |
28 | import org.slf4j.LoggerFactory; | |
29 | ||
30 | import java.io.Serializable; | |
31 | import java.util.Collection; | |
32 | import java.util.Collections; | |
33 | import java.util.Date; | |
34 | ||
35 | /** | |
36 | * Default business-tier implementation of a {@link ValidatingSessionManager}. All session CRUD operations are | |
37 | * delegated to an internal {@link SessionDAO}. | |
38 | * | |
39 | * @since 0.1 | |
40 | */ | |
41 | public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware { | |
42 | ||
43 | //TODO - complete JavaDoc | |
44 | ||
45 | 1 | private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class); |
46 | ||
47 | private SessionFactory sessionFactory; | |
48 | ||
49 | protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager? | |
50 | ||
51 | private CacheManager cacheManager; | |
52 | ||
53 | private boolean deleteInvalidSessions; | |
54 | ||
55 | 44 | public DefaultSessionManager() { |
56 | 44 | this.deleteInvalidSessions = true; |
57 | 44 | this.sessionFactory = new SimpleSessionFactory(); |
58 | 44 | this.sessionDAO = new MemorySessionDAO(); |
59 | 44 | } |
60 | ||
61 | public void setSessionDAO(SessionDAO sessionDAO) { | |
62 | 2 | this.sessionDAO = sessionDAO; |
63 | 2 | applyCacheManagerToSessionDAO(); |
64 | 2 | } |
65 | ||
66 | public SessionDAO getSessionDAO() { | |
67 | 1 | return this.sessionDAO; |
68 | } | |
69 | ||
70 | /** | |
71 | * Returns the {@code SessionFactory} used to generate new {@link Session} instances. The default instance | |
72 | * is a {@link SimpleSessionFactory}. | |
73 | * | |
74 | * @return the {@code SessionFactory} used to generate new {@link Session} instances. | |
75 | * @since 1.0 | |
76 | */ | |
77 | public SessionFactory getSessionFactory() { | |
78 | 28 | return sessionFactory; |
79 | } | |
80 | ||
81 | /** | |
82 | * Sets the {@code SessionFactory} used to generate new {@link Session} instances. The default instance | |
83 | * is a {@link SimpleSessionFactory}. | |
84 | * | |
85 | * @param sessionFactory the {@code SessionFactory} used to generate new {@link Session} instances. | |
86 | * @since 1.0 | |
87 | */ | |
88 | public void setSessionFactory(SessionFactory sessionFactory) { | |
89 | 1 | this.sessionFactory = sessionFactory; |
90 | 1 | } |
91 | ||
92 | /** | |
93 | * Returns {@code true} if sessions should be automatically deleted after they are discovered to be invalid, | |
94 | * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. The | |
95 | * default is {@code true} to ensure no orphans exist in the underlying data store. | |
96 | * <h4>Usage</h4> | |
97 | * It is ok to set this to {@code false} <b><em>ONLY</em></b> if you have some other process that you manage yourself | |
98 | * that periodically deletes invalid sessions from the backing data store over time, such as via a Quartz or Cron | |
99 | * job. If you do not do this, the invalid sessions will become 'orphans' and fill up the data store over time. | |
100 | * <p/> | |
101 | * This property is provided because some systems need the ability to perform querying/reporting against sessions in | |
102 | * the data store, even after they have stopped or expired. Setting this attribute to {@code false} will allow | |
103 | * such querying, but with the caveat that the application developer/configurer deletes the sessions themselves by | |
104 | * some other means (cron, quartz, etc). | |
105 | * | |
106 | * @return {@code true} if sessions should be automatically deleted after they are discovered to be invalid, | |
107 | * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. | |
108 | * @since 1.0 | |
109 | */ | |
110 | public boolean isDeleteInvalidSessions() { | |
111 | 17 | return deleteInvalidSessions; |
112 | } | |
113 | ||
114 | /** | |
115 | * Sets whether or not sessions should be automatically deleted after they are discovered to be invalid. Default | |
116 | * value is {@code true} to ensure no orphans will exist in the underlying data store. | |
117 | * <h4>WARNING</h4> | |
118 | * Only set this value to {@code false} if you are manually going to delete sessions yourself by some process | |
119 | * (quartz, cron, etc) external to Shiro's control. See the | |
120 | * {@link #isDeleteInvalidSessions() isDeleteInvalidSessions()} JavaDoc for more. | |
121 | * | |
122 | * @param deleteInvalidSessions whether or not sessions should be automatically deleted after they are discovered | |
123 | * to be invalid. | |
124 | * @since 1.0 | |
125 | */ | |
126 | @SuppressWarnings({"UnusedDeclaration"}) | |
127 | public void setDeleteInvalidSessions(boolean deleteInvalidSessions) { | |
128 | 0 | this.deleteInvalidSessions = deleteInvalidSessions; |
129 | 0 | } |
130 | ||
131 | public void setCacheManager(CacheManager cacheManager) { | |
132 | 38 | this.cacheManager = cacheManager; |
133 | 38 | applyCacheManagerToSessionDAO(); |
134 | 38 | } |
135 | ||
136 | /** | |
137 | * Sets the internal {@code CacheManager} on the {@code SessionDAO} if it implements the | |
138 | * {@link org.apache.shiro.cache.CacheManagerAware CacheManagerAware} interface. | |
139 | * <p/> | |
140 | * This method is called after setting a cacheManager via the | |
141 | * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) setCacheManager} method <em>em</em> when | |
142 | * setting a {@code SessionDAO} via the {@link #setSessionDAO} method to allow it to be propagated | |
143 | * in either case. | |
144 | * | |
145 | * @since 1.0 | |
146 | */ | |
147 | private void applyCacheManagerToSessionDAO() { | |
148 | 40 | if (this.cacheManager != null && this.sessionDAO != null && this.sessionDAO instanceof CacheManagerAware) { |
149 | 1 | ((CacheManagerAware) this.sessionDAO).setCacheManager(this.cacheManager); |
150 | } | |
151 | 40 | } |
152 | ||
153 | protected Session doCreateSession(SessionContext context) { | |
154 | 28 | Session s = newSessionInstance(context); |
155 | 28 | if (log.isTraceEnabled()) { |
156 | 28 | log.trace("Creating session for host {}", s.getHost()); |
157 | } | |
158 | 28 | create(s); |
159 | 28 | return s; |
160 | } | |
161 | ||
162 | protected Session newSessionInstance(SessionContext context) { | |
163 | 28 | return getSessionFactory().createSession(context); |
164 | } | |
165 | ||
166 | /** | |
167 | * Persists the given session instance to an underlying EIS (Enterprise Information System). This implementation | |
168 | * delegates and calls | |
169 | * <code>this.{@link SessionDAO sessionDAO}.{@link SessionDAO#create(org.apache.shiro.session.Session) create}(session);<code> | |
170 | * | |
171 | * @param session the Session instance to persist to the underlying EIS. | |
172 | */ | |
173 | protected void create(Session session) { | |
174 | 28 | if (log.isDebugEnabled()) { |
175 | 28 | log.debug("Creating new EIS record for new session instance [" + session + "]"); |
176 | } | |
177 | 28 | sessionDAO.create(session); |
178 | 28 | } |
179 | ||
180 | @Override | |
181 | protected void onStop(Session session) { | |
182 | 12 | if (session instanceof SimpleSession) { |
183 | 12 | SimpleSession ss = (SimpleSession) session; |
184 | 12 | Date stopTs = ss.getStopTimestamp(); |
185 | 12 | ss.setLastAccessTime(stopTs); |
186 | } | |
187 | 12 | onChange(session); |
188 | 12 | } |
189 | ||
190 | @Override | |
191 | protected void afterStopped(Session session) { | |
192 | 12 | if (isDeleteInvalidSessions()) { |
193 | 12 | delete(session); |
194 | } | |
195 | 12 | } |
196 | ||
197 | protected void onExpiration(Session session) { | |
198 | 5 | if (session instanceof SimpleSession) { |
199 | 5 | ((SimpleSession) session).setExpired(true); |
200 | } | |
201 | 5 | onChange(session); |
202 | 5 | } |
203 | ||
204 | @Override | |
205 | protected void afterExpired(Session session) { | |
206 | 5 | if (isDeleteInvalidSessions()) { |
207 | 5 | delete(session); |
208 | } | |
209 | 5 | } |
210 | ||
211 | protected void onChange(Session session) { | |
212 | 111 | sessionDAO.update(session); |
213 | 111 | } |
214 | ||
215 | protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException { | |
216 | 203 | Serializable sessionId = getSessionId(sessionKey); |
217 | 203 | if (sessionId == null) { |
218 | 0 | log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " + |
219 | "session could not be found.", sessionKey); | |
220 | 0 | return null; |
221 | } | |
222 | 203 | Session s = retrieveSessionFromDataSource(sessionId); |
223 | 203 | if (s == null) { |
224 | //session ID was provided, meaning one is expected to be found, but we couldn't find one: | |
225 | 0 | String msg = "Could not find session with ID [" + sessionId + "]"; |
226 | 0 | throw new UnknownSessionException(msg); |
227 | } | |
228 | 203 | return s; |
229 | } | |
230 | ||
231 | protected Serializable getSessionId(SessionKey sessionKey) { | |
232 | 203 | return sessionKey.getSessionId(); |
233 | } | |
234 | ||
235 | protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException { | |
236 | 203 | return sessionDAO.readSession(sessionId); |
237 | } | |
238 | ||
239 | protected void delete(Session session) { | |
240 | 17 | sessionDAO.delete(session); |
241 | 17 | } |
242 | ||
243 | protected Collection<Session> getActiveSessions() { | |
244 | 3 | Collection<Session> active = sessionDAO.getActiveSessions(); |
245 | 3 | return active != null ? active : Collections.<Session>emptySet(); |
246 | } | |
247 | ||
248 | } |