View Javadoc

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