/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.servlet.handlers;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.Session;
import io.undertow.server.session.SessionManager;
import io.undertow.servlet.UndertowServletLogger;
import io.undertow.servlet.api.SessionPersistenceManager;
import io.undertow.servlet.core.Lifecycle;
import io.undertow.servlet.spec.HttpSessionImpl;
import io.undertow.servlet.spec.ServletContextImpl;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static io.undertow.servlet.api.SessionPersistenceManager.PersistentSession;

A handler that restores persistent HTTP session state for requests in development mode.

This handler should not be used in production environments.

Author:Stuart Douglas
/** * A handler that restores persistent HTTP session state for requests in development mode. * <p> * This handler should not be used in production environments. * * @author Stuart Douglas */
public class SessionRestoringHandler implements HttpHandler, Lifecycle { private final String deploymentName; private final Map<String, SessionPersistenceManager.PersistentSession> data; private final SessionManager sessionManager; private final ServletContextImpl servletContext; private final HttpHandler next; private final SessionPersistenceManager sessionPersistenceManager; private volatile boolean started = false; public SessionRestoringHandler(String deploymentName, SessionManager sessionManager, ServletContextImpl servletContext, HttpHandler next, SessionPersistenceManager sessionPersistenceManager) { this.deploymentName = deploymentName; this.sessionManager = sessionManager; this.servletContext = servletContext; this.next = next; this.sessionPersistenceManager = sessionPersistenceManager; this.data = new ConcurrentHashMap<>(); } public void start() { ClassLoader old = getTccl(); try { setTccl(servletContext.getClassLoader()); try { final Map<String, SessionPersistenceManager.PersistentSession> sessionData = sessionPersistenceManager.loadSessionAttributes(deploymentName, servletContext.getClassLoader()); if (sessionData != null) { this.data.putAll(sessionData); } } catch (Exception e) { UndertowServletLogger.ROOT_LOGGER.failedtoLoadPersistentSessions(e); } this.started = true; } finally { setTccl(old); } } public void stop() { ClassLoader old = getTccl(); try { setTccl(servletContext.getClassLoader()); this.started = false; final Map<String, SessionPersistenceManager.PersistentSession> objectData = new HashMap<>(); for (String sessionId : sessionManager.getTransientSessions()) { Session session = sessionManager.getSession(sessionId); if (session != null) { final HttpSessionEvent event = new HttpSessionEvent(SecurityActions.forSession(session, servletContext, false)); final Map<String, Object> sessionData = new HashMap<>(); for (String attr : session.getAttributeNames()) { final Object attribute = session.getAttribute(attr); sessionData.put(attr, attribute); if (attribute instanceof HttpSessionActivationListener) { ((HttpSessionActivationListener) attribute).sessionWillPassivate(event); } } objectData.put(sessionId, new PersistentSession(new Date(session.getLastAccessedTime() + (session.getMaxInactiveInterval() * 1000)), sessionData)); } } sessionPersistenceManager.persistSessions(deploymentName, objectData); this.data.clear(); } finally { setTccl(old); } } @Override public void handleRequest(HttpServerExchange exchange) throws Exception { final String incomingSessionId = servletContext.getSessionConfig().findSessionId(exchange); if (incomingSessionId == null || !data.containsKey(incomingSessionId)) { next.handleRequest(exchange); return; } //we have some old data PersistentSession result = data.remove(incomingSessionId); if (result != null) { long time = System.currentTimeMillis(); if (time < result.getExpiration().getTime()) { final HttpSessionImpl session = servletContext.getSession(exchange, true); final HttpSessionEvent event = new HttpSessionEvent(session); for (Map.Entry<String, Object> entry : result.getSessionData().entrySet()) { if (entry.getValue() instanceof HttpSessionActivationListener) { ((HttpSessionActivationListener) entry.getValue()).sessionDidActivate(event); } if(entry.getKey().startsWith(HttpSessionImpl.IO_UNDERTOW)) { session.getSession().setAttribute(entry.getKey(), entry.getValue()); } else { session.setAttribute(entry.getKey(), entry.getValue()); } } } } next.handleRequest(exchange); } @Override public boolean isStarted() { return started; } private ClassLoader getTccl() { if (System.getSecurityManager() == null) { return Thread.currentThread().getContextClassLoader(); } else { return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { @Override public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } }); } } private void setTccl(final ClassLoader classLoader) { if (System.getSecurityManager() == null) { Thread.currentThread().setContextClassLoader(classLoader); } else { AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { Thread.currentThread().setContextClassLoader(classLoader); return null; } }); } } }