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 module hunt.shiro.session.mgt.AbstractNativeSessionManager; 20 21 import hunt.shiro.session.mgt.AbstractSessionManager; 22 import hunt.shiro.session.mgt.DefaultSessionKey; 23 import hunt.shiro.session.mgt.DelegatingSession; 24 import hunt.shiro.session.mgt.ImmutableProxiedSession; 25 import hunt.shiro.session.mgt.NativeSessionManager; 26 import hunt.shiro.session.mgt.SessionContext; 27 import hunt.shiro.session.mgt.SessionKey; 28 29 import hunt.shiro.Exceptions; 30 import hunt.shiro.event.EventBus; 31 import hunt.shiro.event.EventBusAware; 32 import hunt.shiro.session.Session; 33 34 import hunt.shiro.session.SessionListener; 35 import hunt.shiro.util.CollectionUtils; 36 37 import hunt.Exceptions; 38 import hunt.collection; 39 import hunt.logging.Logger; 40 41 /** 42 * Abstract implementation supporting the {@link NativeSessionManager NativeSessionManager} interface, supporting 43 * {@link SessionListener SessionListener}s and application of the 44 * {@link #getGlobalSessionTimeout() globalSessionTimeout}. 45 * 46 */ 47 abstract class AbstractNativeSessionManager : 48 AbstractSessionManager, NativeSessionManager, EventBusAware { 49 50 private EventBus eventBus; 51 52 private Collection!(SessionListener) listeners; 53 54 this() { 55 this.listeners = new ArrayList!(SessionListener)(); 56 } 57 58 void setSessionListeners(Collection!(SessionListener) listeners) { 59 this.listeners = listeners !is null ? listeners : new ArrayList!(SessionListener)(); 60 } 61 62 //@SuppressWarnings({"UnusedDeclaration"}) 63 Collection!(SessionListener) getSessionListeners() { 64 return this.listeners; 65 } 66 67 /** 68 * Returns the EventBus used to publish SessionEvents. 69 * 70 * @return the EventBus used to publish SessionEvents. 71 */ 72 protected EventBus getEventBus() { 73 return eventBus; 74 } 75 76 /** 77 * Sets the EventBus to use to publish SessionEvents. 78 * 79 * @param eventBus the EventBus to use to publish SessionEvents. 80 */ 81 void setEventBus(EventBus eventBus) { 82 this.eventBus = eventBus; 83 } 84 85 /** 86 * Publishes events on the event bus if the event bus is non-null, otherwise does nothing. 87 * 88 * @param event the event to publish on the event bus if the event bus exists. 89 */ 90 protected void publishEvent(Object event) { 91 if (this.eventBus !is null) { 92 this.eventBus.publish(event); 93 } 94 } 95 96 Session start(SessionContext context) { 97 Session session = createSession(context); 98 applyGlobalSessionTimeout(session); 99 onStart(session, context); 100 notifyStart(session); 101 //Don't expose the EIS-tier Session object to the client-tier: 102 return createExposedSession(session, context); 103 } 104 105 /** 106 * Creates a new {@code Session Session} instance based on the specified (possibly {@code null}) 107 * initialization data. Implementing classes must manage the persistent state of the returned session such that it 108 * could later be acquired via the {@link #getSession(SessionKey)} method. 109 * 110 * @param context the initialization data that can be used by the implementation or underlying 111 * {@link SessionFactory} when instantiating the internal {@code Session} instance. 112 * @return the new {@code Session} instance. 113 * @throws hunt.shiro.authz.HostUnauthorizedException 114 * if the system access control policy restricts access based 115 * on client location/IP and the specified hostAddress hasn't been enabled. 116 * @throws AuthorizationException if the system access control policy does not allow the currently executing 117 * caller to start sessions. 118 */ 119 protected abstract Session createSession(SessionContext context); 120 121 protected void applyGlobalSessionTimeout(Session session) { 122 session.setTimeout(getGlobalSessionTimeout()); 123 onChange(session); 124 } 125 126 /** 127 * Template method that allows subclasses to react to a new session being created. 128 * <p/> 129 * This method is invoked <em>before</em> any session listeners are notified. 130 * 131 * @param session the session that was just {@link #createSession created}. 132 * @param context the {@link SessionContext SessionContext} that was used to start the session. 133 */ 134 protected void onStart(Session session, SessionContext context) { 135 } 136 137 Session getSession(SessionKey key){ 138 Session session = lookupSession(key); 139 return session !is null ? createExposedSession(session, key) : null; 140 } 141 142 private Session lookupSession(SessionKey key){ 143 if (key is null) { 144 throw new NullPointerException("SessionKey argument cannot be null."); 145 } 146 return doGetSession(key); 147 } 148 149 private Session lookupRequiredSession(SessionKey key){ 150 Session session = lookupSession(key); 151 if (session is null) { 152 string msg = "Unable to locate required Session instance based on SessionKey [" ~ 153 (cast(Object)key).toString() ~ "]."; 154 throw new UnknownSessionException(msg); 155 } 156 return session; 157 } 158 159 protected abstract Session doGetSession(SessionKey key); 160 161 protected Session createExposedSession(Session session, SessionContext context) { 162 return new DelegatingSession(this, new DefaultSessionKey(session.getId())); 163 } 164 165 protected Session createExposedSession(Session session, SessionKey key) { 166 return new DelegatingSession(this, new DefaultSessionKey(session.getId())); 167 } 168 169 /** 170 * Returns the session instance to use to pass to registered {@code SessionListener}s for notification 171 * that the session has been invalidated (stopped or expired). 172 * <p/> 173 * The default implementation returns an {@link ImmutableProxiedSession ImmutableProxiedSession} instance to ensure 174 * that the specified {@code session} argument is not modified by any listeners. 175 * 176 * @param session the {@code Session} object being invalidated. 177 * @return the {@code Session} instance to use to pass to registered {@code SessionListener}s for notification. 178 */ 179 protected Session beforeInvalidNotification(Session session) { 180 return new ImmutableProxiedSession(session); 181 } 182 183 /** 184 * Notifies any interested {@link SessionListener}s that a Session has started. This method is invoked 185 * <em>after</em> the {@link #onStart onStart} method is called. 186 * 187 * @param session the session that has just started that will be delivered to any 188 * {@link #setSessionListeners(java.util.Collection) registered} session listeners. 189 * @see SessionListener#onStart(hunt.shiro.session.Session) 190 */ 191 protected void notifyStart(Session session) { 192 foreach(SessionListener listener ; this.listeners) { 193 listener.onStart(session); 194 } 195 } 196 197 protected void notifyStop(Session session) { 198 Session forNotification = beforeInvalidNotification(session); 199 foreach(SessionListener listener ; this.listeners) { 200 listener.onStop(forNotification); 201 } 202 } 203 204 protected void notifyExpiration(Session session) { 205 Session forNotification = beforeInvalidNotification(session); 206 foreach(SessionListener listener ; this.listeners) { 207 listener.onExpiration(forNotification); 208 } 209 } 210 211 Date getStartTimestamp(SessionKey key) { 212 return lookupRequiredSession(key).getStartTimestamp(); 213 } 214 215 Date getLastAccessTime(SessionKey key) { 216 return lookupRequiredSession(key).getLastAccessTime(); 217 } 218 219 long getTimeout(SessionKey key){ 220 return lookupRequiredSession(key).getTimeout(); 221 } 222 223 void setTimeout(SessionKey key, long maxIdleTimeInMillis){ 224 Session s = lookupRequiredSession(key); 225 s.setTimeout(maxIdleTimeInMillis); 226 onChange(s); 227 } 228 229 void touch(SessionKey key){ 230 Session s = lookupRequiredSession(key); 231 s.touch(); 232 onChange(s); 233 } 234 235 string getHost(SessionKey key) { 236 return lookupRequiredSession(key).getHost(); 237 } 238 239 Object[] getAttributeKeys(SessionKey key) { 240 Object[] c = lookupRequiredSession(key).getAttributeKeys(); 241 return c; 242 } 243 244 Object getAttribute(SessionKey sessionKey, Object attributeKey){ 245 return lookupRequiredSession(sessionKey).getAttribute(attributeKey); 246 } 247 248 void setAttribute(SessionKey sessionKey, Object attributeKey, Object value){ 249 if (value is null) { 250 removeAttribute(sessionKey, attributeKey); 251 } else { 252 Session s = lookupRequiredSession(sessionKey); 253 s.setAttribute(attributeKey, value); 254 onChange(s); 255 } 256 } 257 258 Object removeAttribute(SessionKey sessionKey, Object attributeKey){ 259 Session s = lookupRequiredSession(sessionKey); 260 Object removed = s.removeAttribute(attributeKey); 261 if (removed !is null) { 262 onChange(s); 263 } 264 return removed; 265 } 266 267 bool isValid(SessionKey key) { 268 try { 269 checkValid(key); 270 return true; 271 } catch (InvalidSessionException e) { 272 return false; 273 } 274 } 275 276 void stop(SessionKey key){ 277 Session session = lookupRequiredSession(key); 278 try { 279 version(HUNT_SHIRO_DEBUG) { 280 tracef("Stopping session with id [" ~ session.getId() ~ "]"); 281 } 282 session.stop(); 283 onStop(session, key); 284 notifyStop(session); 285 } finally { 286 afterStopped(session); 287 } 288 } 289 290 protected void onStop(Session session, SessionKey key) { 291 onStop(session); 292 } 293 294 protected void onStop(Session session) { 295 onChange(session); 296 } 297 298 protected void afterStopped(Session session) { 299 } 300 301 void checkValid(SessionKey key){ 302 //just try to acquire it. If there is a problem, an exception will be thrown: 303 lookupRequiredSession(key); 304 } 305 306 protected void onChange(Session s) { 307 } 308 }