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