1
18 package net.bull.javamelody;
19
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.atomic.AtomicInteger;
30
31 import javax.servlet.ServletContext;
32 import javax.servlet.ServletContextEvent;
33 import javax.servlet.ServletContextListener;
34 import javax.servlet.http.HttpSession;
35 import javax.servlet.http.HttpSessionActivationListener;
36 import javax.servlet.http.HttpSessionEvent;
37 import javax.servlet.http.HttpSessionListener;
38
39 import net.bull.javamelody.internal.common.HttpParameter;
40 import net.bull.javamelody.internal.common.LOG;
41 import net.bull.javamelody.internal.common.Parameters;
42 import net.bull.javamelody.internal.model.SessionInformations;
43
44
51 public class SessionListener implements HttpSessionListener, HttpSessionActivationListener,
52 ServletContextListener, Serializable {
53 public static final String CSRF_TOKEN_SESSION_NAME = "javamelody."
54 + HttpParameter.TOKEN.getName();
55 public static final String SESSION_COUNTRY_KEY = "javamelody.country";
56 public static final String SESSION_REMOTE_ADDR = "javamelody.remoteAddr";
57 public static final String SESSION_REMOTE_USER = "javamelody.remoteUser";
58 public static final String SESSION_USER_AGENT = "javamelody.userAgent";
59
60 private static final String SESSION_ACTIVATION_KEY = "javamelody.sessionActivation";
61
62 private static final long serialVersionUID = -1624944319058843901L;
63
64 private static final AtomicInteger SESSION_COUNT = new AtomicInteger();
65
66 @SuppressWarnings("all")
67 private static final List<String> CONTEXT_PATHS = new ArrayList<>();
68
69
70 @SuppressWarnings("all")
71 private static final ConcurrentMap<String, HttpSession> SESSION_MAP_BY_ID = new ConcurrentHashMap<>();
72
73 private static final ThreadLocal<HttpSession> SESSION_CONTEXT = new ThreadLocal<>();
74
75 private static boolean instanceCreated;
76
77 private boolean instanceEnabled;
78
79 static final class SessionInformationsComparator
80 implements Comparator<SessionInformations>, Serializable {
81 private static final long serialVersionUID = 1L;
82
83
84 @Override
85 public int compare(SessionInformations session1, SessionInformations session2) {
86 if (session1.getLastAccess().before(session2.getLastAccess())) {
87 return 1;
88 } else if (session1.getLastAccess().after(session2.getLastAccess())) {
89 return -1;
90 } else {
91 return 0;
92 }
93 }
94 }
95
96
99 public SessionListener() {
100 super();
101 if (instanceCreated) {
102
103
104
105
106 instanceEnabled = false;
107 } else {
108 instanceEnabled = true;
109 setInstanceCreated(true);
110 }
111 }
112
113
117 public SessionListener(boolean instanceEnabled) {
118 super();
119 this.instanceEnabled = instanceEnabled;
120 setInstanceCreated(true);
121 }
122
123 private static void setInstanceCreated(boolean newInstanceCreated) {
124 instanceCreated = newInstanceCreated;
125 }
126
127 public static int getSessionCount() {
128 if (!instanceCreated) {
129 return -1;
130 }
131
132
133
134 return SESSION_COUNT.get();
135 }
136
137 public static long getSessionAgeSum() {
138 if (!instanceCreated) {
139 return -1;
140 }
141 final long now = System.currentTimeMillis();
142 long result = 0;
143 for (final HttpSession session : SESSION_MAP_BY_ID.values()) {
144 try {
145 result += now - session.getCreationTime();
146 } catch (final Exception e) {
147
148 continue;
149 }
150 }
151 return result;
152 }
153
154
155
156 static void invalidateAllSessions() {
157 invalidateAllSessionsExceptCurrentSession(null);
158 }
159
160
161 public static void invalidateAllSessionsExceptCurrentSession(HttpSession currentSession) {
162 for (final HttpSession session : SESSION_MAP_BY_ID.values()) {
163 try {
164 if (currentSession != null && currentSession.getId().equals(session.getId())) {
165
166
167 continue;
168 }
169 session.invalidate();
170 } catch (final Exception e) {
171
172 continue;
173 }
174 }
175 }
176
177 public static void invalidateSession(String sessionId) {
178 final HttpSession session = getSessionById(sessionId);
179 if (session != null) {
180
181 try {
182 session.invalidate();
183 } catch (final Exception e) {
184
185 return;
186 }
187 }
188 }
189
190 private static HttpSession getSessionById(String sessionId) {
191 final HttpSession session = SESSION_MAP_BY_ID.get(sessionId);
192 if (session == null) {
193
194
195 for (final HttpSession other : SESSION_MAP_BY_ID.values()) {
196 if (other.getId().equals(sessionId)) {
197 return other;
198 }
199 }
200 }
201 return session;
202 }
203
204 private static void removeSessionsWithChangedId() {
205 for (final Map.Entry<String, HttpSession> entry : SESSION_MAP_BY_ID.entrySet()) {
206 final String id = entry.getKey();
207 final HttpSession other = entry.getValue();
208 if (!id.equals(other.getId())) {
209 SESSION_MAP_BY_ID.remove(id);
210 }
211 }
212 }
213
214 private static void fixSessionsWithChangedId() {
215 for (final Map.Entry<String, HttpSession> entry : SESSION_MAP_BY_ID.entrySet()) {
216 final String id = entry.getKey();
217 final HttpSession other = entry.getValue();
218 if (!id.equals(other.getId())) {
219 SESSION_MAP_BY_ID.remove(id);
220 SESSION_MAP_BY_ID.put(other.getId(), other);
221 }
222 }
223 }
224
225 private static void addSession(final HttpSession session) {
226 SESSION_MAP_BY_ID.put(session.getId(), session);
227 }
228
229 private static void removeSession(final HttpSession session) {
230 final HttpSession removedSession = SESSION_MAP_BY_ID.remove(session.getId());
231 if (removedSession == null) {
232
233
234 fixSessionsWithChangedId();
235 SESSION_MAP_BY_ID.remove(session.getId());
236 }
237 }
238
239 public static List<SessionInformations> getAllSessionsInformations() {
240 final Collection<HttpSession> sessions = SESSION_MAP_BY_ID.values();
241 final List<SessionInformations> sessionsInformations = new ArrayList<>(sessions.size());
242 for (final HttpSession session : sessions) {
243 try {
244 sessionsInformations.add(new SessionInformations(session, false));
245 } catch (final Exception e) {
246
247 continue;
248 }
249 }
250 sortSessions(sessionsInformations);
251 return Collections.unmodifiableList(sessionsInformations);
252 }
253
254 public static void sortSessions(List<SessionInformations> sessionsInformations) {
255 if (sessionsInformations.size() > 1) {
256 Collections.sort(sessionsInformations,
257 Collections.reverseOrder(new SessionInformationsComparator()));
258 }
259 }
260
261 public static SessionInformations getSessionInformationsBySessionId(String sessionId) {
262 final HttpSession session = getSessionById(sessionId);
263 if (session == null) {
264 return null;
265 }
266
267 try {
268 return new SessionInformations(session, true);
269 } catch (final Exception e) {
270
271 return null;
272 }
273 }
274
275
279 public static void bindSession(HttpSession session) {
280 if (session != null) {
281 SESSION_CONTEXT.set(session);
282 }
283 }
284
285
289 public static HttpSession getCurrentSession() {
290 return SESSION_CONTEXT.get();
291 }
292
293
296 public static void unbindSession() {
297 SESSION_CONTEXT.remove();
298 }
299
300
301 @Override
302 public void contextInitialized(ServletContextEvent event) {
303 final long start = System.currentTimeMillis();
304
305
306
307 System.getProperty("java.io.tmpdir");
308
309 final String contextPath = Parameters.getContextPath(event.getServletContext());
310 if (!instanceEnabled) {
311 if (!CONTEXT_PATHS.contains(contextPath)) {
312
313 instanceEnabled = true;
314 } else {
315 return;
316 }
317 }
318 CONTEXT_PATHS.add(contextPath);
319
320 Parameters.initialize(event.getServletContext());
321
322 LOG.debug("JavaMelody listener init started");
323
324
325
326 final JdbcWrapper jdbcWrapper = JdbcWrapper.SINGLETON;
327 jdbcWrapper.initServletContext(event.getServletContext());
328 if (!Parameters.isNoDatabase()) {
329 jdbcWrapper.rebindDataSources();
330 }
331
332 final long duration = System.currentTimeMillis() - start;
333 LOG.debug("JavaMelody listener init done in " + duration + " ms");
334 }
335
336
337 @Override
338 public void contextDestroyed(ServletContextEvent event) {
339 if (!instanceEnabled) {
340 return;
341 }
342
343 SESSION_MAP_BY_ID.clear();
344 SESSION_COUNT.set(0);
345
346
347
348 JdbcWrapper.SINGLETON.stop();
349
350
351 if (event.getServletContext().getClass().getName().startsWith("io.undertow")) {
352
353 Parameters.initialize((ServletContext) null);
354 }
355
356 LOG.debug("JavaMelody listener destroy done");
357 }
358
359
360
361
362
363
364
365 @Override
366 public void sessionCreated(HttpSessionEvent event) {
367 if (!instanceEnabled) {
368 return;
369 }
370
371 final HttpSession session = event.getSession();
372
373
374
375
376
377
378
379
380
381 if (session.getAttribute(SESSION_ACTIVATION_KEY) != null) {
382
383
384
385 removeSessionsWithChangedId();
386 } else {
387 session.setAttribute(SESSION_ACTIVATION_KEY, this);
388
389
390 SESSION_COUNT.incrementAndGet();
391 }
392
393
394 addSession(session);
395 }
396
397
398 @Override
399 public void sessionDestroyed(HttpSessionEvent event) {
400 if (!instanceEnabled) {
401 return;
402 }
403 final HttpSession session = event.getSession();
404
405
406
407
408
409
410 SESSION_COUNT.decrementAndGet();
411
412
413 removeSession(session);
414 }
415
416
417 @Override
418 public void sessionDidActivate(HttpSessionEvent event) {
419 if (!instanceEnabled) {
420 return;
421 }
422
423 SESSION_COUNT.incrementAndGet();
424
425
426 addSession(event.getSession());
427 }
428
429
430 @Override
431 public void sessionWillPassivate(HttpSessionEvent event) {
432 if (!instanceEnabled) {
433 return;
434 }
435
436 SESSION_COUNT.decrementAndGet();
437
438
439 removeSession(event.getSession());
440 }
441
442
443 void registerSessionIfNeeded(HttpSession session) {
444 if (session != null) {
445 synchronized (session) {
446 if (!SESSION_MAP_BY_ID.containsKey(session.getId())) {
447 sessionCreated(new HttpSessionEvent(session));
448 }
449 }
450 }
451 }
452
453
454 void unregisterSessionIfNeeded(HttpSession session) {
455 if (session != null) {
456 try {
457 session.getCreationTime();
458
459
460
461 session.getLastAccessedTime();
462 } catch (final IllegalStateException e) {
463
464 synchronized (session) {
465 sessionDestroyed(new HttpSessionEvent(session));
466 }
467 }
468 }
469 }
470
471
472 void unregisterInvalidatedSessions() {
473 for (final Map.Entry<String, HttpSession> entry : SESSION_MAP_BY_ID.entrySet()) {
474 final HttpSession session = entry.getValue();
475 if (session.getId() != null) {
476 unregisterSessionIfNeeded(session);
477 } else {
478
479 final String sessionId = entry.getKey();
480 SESSION_MAP_BY_ID.remove(sessionId);
481 }
482 }
483
484
485
486 SESSION_COUNT.set(SESSION_MAP_BY_ID.size());
487 }
488
489 void removeAllActivationListeners() {
490 for (final HttpSession session : SESSION_MAP_BY_ID.values()) {
491 try {
492 session.removeAttribute(SESSION_ACTIVATION_KEY);
493 } catch (final Exception e) {
494
495 continue;
496 }
497 }
498 }
499
500
501 @Override
502 public String toString() {
503 return getClass().getSimpleName() + "[sessionCount=" + getSessionCount() + ']';
504 }
505 }
506