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.session.ExpiredSessionException;
22  import org.apache.shiro.session.InvalidSessionException;
23  import org.apache.shiro.session.Session;
24  import org.apache.shiro.session.SessionListener;
25  import org.apache.shiro.session.SessionListenerAdapter;
26  import org.apache.shiro.session.mgt.eis.SessionDAO;
27  import org.apache.shiro.util.ThreadContext;
28  import org.easymock.EasyMock;
29  import org.easymock.IArgumentMatcher;
30  import org.junit.After;
31  import org.junit.Before;
32  import org.junit.Test;
33  
34  import java.util.UUID;
35  
36  import static org.easymock.EasyMock.*;
37  import static org.junit.Assert.*;
38  
39  /**
40   * Unit test for the {@link DefaultSessionManager DefaultSessionManager} implementation.
41   */
42  public class DefaultSessionManagerTest {
43  
44      DefaultSessionManager sm = null;
45  
46      @Before
47      public void setup() {
48          ThreadContext.remove();
49          sm = new DefaultSessionManager();
50      }
51  
52      @After
53      public void tearDown() {
54          sm.destroy();
55          ThreadContext.remove();
56      }
57  
58      public void sleep(long millis) {
59          try {
60              Thread.sleep(millis);
61          } catch (InterruptedException e) {
62              throw new IllegalStateException(e);
63          }
64      }
65  
66      @Test
67      public void testGlobalTimeout() {
68          long timeout = 1000;
69          sm.setGlobalSessionTimeout(timeout);
70          Session session = sm.start(null);
71          assertNotNull(session);
72          assertNotNull(session.getId());
73          assertEquals(session.getTimeout(), timeout);
74      }
75  
76      @Test
77      public void testSessionListenerStartNotification() {
78          final boolean[] started = new boolean[1];
79          SessionListener listener = new SessionListenerAdapter() {
80              public void onStart(Session session) {
81                  started[0] = true;
82              }
83          };
84          sm.getSessionListeners().add(listener);
85          sm.start(null);
86          assertTrue(started[0]);
87      }
88  
89      @Test
90      public void testSessionListenerStopNotification() {
91          final boolean[] stopped = new boolean[1];
92          SessionListener listener = new SessionListenerAdapter() {
93              public void onStop(Session session) {
94                  stopped[0] = true;
95              }
96          };
97          sm.getSessionListeners().add(listener);
98          Session session = sm.start(null);
99          sm.stop(new DefaultSessionKey(session.getId()));
100         assertTrue(stopped[0]);
101     }
102 
103     //asserts fix for SHIRO-388:
104     //Ensures that a session attribute can be accessed in the listener without
105     //causing a stack overflow exception.
106     @Test
107     public void testSessionListenerStopNotificationWithReadAttribute() {
108         final boolean[] stopped = new boolean[1];
109         final String[] value = new String[1];
110         SessionListener listener = new SessionListenerAdapter() {
111             public void onStop(Session session) {
112                 stopped[0] = true;
113                 value[0] = (String)session.getAttribute("foo");
114             }
115         };
116         sm.getSessionListeners().add(listener);
117         Session session = sm.start(null);
118         session.setAttribute("foo", "bar");
119 
120         sm.stop(new DefaultSessionKey(session.getId()));
121 
122         assertTrue(stopped[0]);
123         assertEquals("bar", value[0]);
124     }
125 
126     @Test
127     public void testSessionListenerExpiredNotification() {
128         final boolean[] expired = new boolean[1];
129         SessionListener listener = new SessionListenerAdapter() {
130             public void onExpiration(Session session) {
131                 expired[0] = true;
132             }
133         };
134         sm.getSessionListeners().add(listener);
135         sm.setGlobalSessionTimeout(100);
136         Session session = sm.start(null);
137         sleep(150);
138         try {
139             sm.checkValid(new DefaultSessionKey(session.getId()));
140             fail("check should have thrown an exception.");
141         } catch (InvalidSessionException expected) {
142             //do nothing - expected.
143         }
144         assertTrue(expired[0]);
145     }
146 
147     @Test
148     public void testSessionDeleteOnExpiration() {
149         sm.setGlobalSessionTimeout(100);
150 
151         SessionDAO sessionDAO = createMock(SessionDAO.class);
152         sm.setSessionDAO(sessionDAO);
153 
154         String sessionId1 = UUID.randomUUID().toString();
155         final SimpleSession session1 = new SimpleSession();
156         session1.setId(sessionId1);
157 
158         final Session[] activeSession = new SimpleSession[]{session1};
159         sm.setSessionFactory(new SessionFactory() {
160             public Session createSession(SessionContext initData) {
161                 return activeSession[0];
162             }
163         });
164 
165         expect(sessionDAO.create(eq(session1))).andReturn(sessionId1);
166         sessionDAO.update(eq(session1));
167         expectLastCall().anyTimes();
168         replay(sessionDAO);
169         Session session = sm.start(null);
170         assertNotNull(session);
171         verify(sessionDAO);
172         reset(sessionDAO);
173 
174         expect(sessionDAO.readSession(sessionId1)).andReturn(session1).anyTimes();
175         sessionDAO.update(eq(session1));
176         replay(sessionDAO);
177         sm.setTimeout(new DefaultSessionKey(sessionId1), 1);
178         verify(sessionDAO);
179         reset(sessionDAO);
180 
181         sleep(20);
182 
183         expect(sessionDAO.readSession(sessionId1)).andReturn(session1);
184         sessionDAO.update(eq(session1)); //update's the stop timestamp
185         sessionDAO.delete(session1);
186         replay(sessionDAO);
187 
188         //Try to access the same session, but it should throw an UnknownSessionException due to timeout:
189         try {
190             sm.getTimeout(new DefaultSessionKey(sessionId1));
191             fail("Session with id [" + sessionId1 + "] should have expired due to timeout.");
192         } catch (ExpiredSessionException expected) {
193             //expected
194         }
195 
196         verify(sessionDAO); //verify that the delete call was actually made on the DAO
197     }
198 
199     /**
200      * Tests a bug introduced by SHIRO-443, where a custom sessionValidationScheduler would not be started.
201      */
202     @Test
203     public void testEnablingOfCustomSessionValidationScheduler() {
204 
205         // using the default impl of sessionValidationScheduler, as the but effects any scheduler we set directly via
206         // sessionManager.setSessionValidationScheduler(), commonly used in INI configuration.
207         ExecutorServiceSessionValidationScheduler sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
208         DefaultSessionManager sessionManager = new DefaultSessionManager();
209         sessionManager.setSessionValidationScheduler(sessionValidationScheduler);
210 
211         // starting a session will trigger the starting of the validator
212         try {
213             Session session = sessionManager.start(null);
214 
215             // now sessionValidationScheduler should be enabled
216             assertTrue("sessionValidationScheduler was not enabled", sessionValidationScheduler.isEnabled());
217         }
218         finally {
219             // cleanup after test
220             sessionManager.destroy();
221         }
222     }
223 
224     public static <T extends Session> T eqSessionTimeout(long timeout) {
225         EasyMock.reportMatcher(new SessionTimeoutMatcher(timeout));
226         return null;
227     }
228 
229     private static class SessionTimeoutMatcher implements IArgumentMatcher {
230 
231         private final long timeout;
232 
233         public SessionTimeoutMatcher(long timeout) {
234             this.timeout = timeout;
235         }
236 
237         public void appendTo(StringBuffer buffer) {
238             buffer.append("eqSession(timeout=").append(this.timeout).append(")");
239         }
240 
241         public boolean matches(Object o) {
242             return o instanceof Session && ((Session) o).getTimeout() == this.timeout;
243         }
244     }
245 }