1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * 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, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.servlet.handlers;
20
21 import io.undertow.server.HttpHandler;
22 import io.undertow.server.HttpServerExchange;
23 import io.undertow.server.session.Session;
24 import io.undertow.server.session.SessionManager;
25 import io.undertow.servlet.UndertowServletLogger;
26 import io.undertow.servlet.api.SessionPersistenceManager;
27 import io.undertow.servlet.core.Lifecycle;
28 import io.undertow.servlet.spec.HttpSessionImpl;
29 import io.undertow.servlet.spec.ServletContextImpl;
30
31 import javax.servlet.http.HttpSessionActivationListener;
32 import javax.servlet.http.HttpSessionEvent;
33 import java.security.AccessController;
34 import java.security.PrivilegedAction;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.concurrent.ConcurrentHashMap;
39
40 import static io.undertow.servlet.api.SessionPersistenceManager.PersistentSession;
41
42 /**
43  * A handler that restores persistent HTTP session state for requests in development mode.
44  * <p>
45  * This handler should not be used in production environments.
46  *
47  * @author Stuart Douglas
48  */

49 public class SessionRestoringHandler implements HttpHandler, Lifecycle {
50
51     private final String deploymentName;
52     private final Map<String, SessionPersistenceManager.PersistentSession> data;
53     private final SessionManager sessionManager;
54     private final ServletContextImpl servletContext;
55     private final HttpHandler next;
56     private final SessionPersistenceManager sessionPersistenceManager;
57     private volatile boolean started = false;
58
59     public SessionRestoringHandler(String deploymentName, SessionManager sessionManager, ServletContextImpl servletContext, HttpHandler next, SessionPersistenceManager sessionPersistenceManager) {
60         this.deploymentName = deploymentName;
61         this.sessionManager = sessionManager;
62         this.servletContext = servletContext;
63         this.next = next;
64         this.sessionPersistenceManager = sessionPersistenceManager;
65         this.data = new ConcurrentHashMap<>();
66     }
67
68     public void start() {
69         ClassLoader old = getTccl();
70         try {
71             setTccl(servletContext.getClassLoader());
72
73             try {
74                 final Map<String, SessionPersistenceManager.PersistentSession> sessionData = sessionPersistenceManager.loadSessionAttributes(deploymentName, servletContext.getClassLoader());
75                 if (sessionData != null) {
76                     this.data.putAll(sessionData);
77                 }
78             } catch (Exception e) {
79                 UndertowServletLogger.ROOT_LOGGER.failedtoLoadPersistentSessions(e);
80             }
81             this.started = true;
82         } finally {
83             setTccl(old);
84         }
85     }
86
87     public void stop() {
88         ClassLoader old = getTccl();
89         try {
90             setTccl(servletContext.getClassLoader());
91             this.started = false;
92             final Map<String, SessionPersistenceManager.PersistentSession> objectData = new HashMap<>();
93             for (String sessionId : sessionManager.getTransientSessions()) {
94                 Session session = sessionManager.getSession(sessionId);
95                 if (session != null) {
96                     final HttpSessionEvent event = new HttpSessionEvent(SecurityActions.forSession(session, servletContext, false));
97                     final Map<String, Object> sessionData = new HashMap<>();
98                     for (String attr : session.getAttributeNames()) {
99                         final Object attribute = session.getAttribute(attr);
100                         sessionData.put(attr, attribute);
101                         if (attribute instanceof HttpSessionActivationListener) {
102                             ((HttpSessionActivationListener) attribute).sessionWillPassivate(event);
103                         }
104                     }
105                     objectData.put(sessionId, new PersistentSession(new Date(session.getLastAccessedTime() + (session.getMaxInactiveInterval() * 1000)), sessionData));
106                 }
107             }
108             sessionPersistenceManager.persistSessions(deploymentName, objectData);
109             this.data.clear();
110         } finally {
111             setTccl(old);
112         }
113     }
114
115     @Override
116     public void handleRequest(HttpServerExchange exchange) throws Exception {
117         final String incomingSessionId = servletContext.getSessionConfig().findSessionId(exchange);
118         if (incomingSessionId == null || !data.containsKey(incomingSessionId)) {
119             next.handleRequest(exchange);
120             return;
121         }
122
123         //we have some old data
124         PersistentSession result = data.remove(incomingSessionId);
125         if (result != null) {
126             long time = System.currentTimeMillis();
127             if (time < result.getExpiration().getTime()) {
128                 final HttpSessionImpl session = servletContext.getSession(exchange, true);
129                 final HttpSessionEvent event = new HttpSessionEvent(session);
130                 for (Map.Entry<String, Object> entry : result.getSessionData().entrySet()) {
131
132                     if (entry.getValue() instanceof HttpSessionActivationListener) {
133                         ((HttpSessionActivationListener) entry.getValue()).sessionDidActivate(event);
134                     }
135                     if(entry.getKey().startsWith(HttpSessionImpl.IO_UNDERTOW)) {
136                         session.getSession().setAttribute(entry.getKey(), entry.getValue());
137                     } else {
138                         session.setAttribute(entry.getKey(), entry.getValue());
139                     }
140                 }
141             }
142         }
143         next.handleRequest(exchange);
144     }
145
146     @Override
147     public boolean isStarted() {
148         return started;
149     }
150
151     private ClassLoader getTccl() {
152         if (System.getSecurityManager() == null) {
153             return Thread.currentThread().getContextClassLoader();
154         } else {
155             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
156                 @Override
157                 public ClassLoader run() {
158                     return Thread.currentThread().getContextClassLoader();
159                 }
160             });
161         }
162     }
163
164     private void setTccl(final ClassLoader classLoader) {
165         if (System.getSecurityManager() == null) {
166             Thread.currentThread().setContextClassLoader(classLoader);
167         } else {
168             AccessController.doPrivileged(new PrivilegedAction<Void>() {
169                 @Override
170                 public Void run() {
171                     Thread.currentThread().setContextClassLoader(classLoader);
172                     return null;
173                 }
174             });
175         }
176     }
177 }
178