Allow persistent servlet sessions across restarts

Update Tomcat, Jetty and Undertow to serialize session data when the
application is stopped and load it again when the application restarts.

Persistent session are opt-in; either by setting `persistentSession`
on the ConfigurableEmbeddedServletContainer or by using the property
`server.session.persistent=true`.

Fixes gh-2490
pull/3531/head
Phillip Webb 9 years ago
parent 3e72300bd4
commit 08d1f6daf5

@ -262,6 +262,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
if (getSession().getTimeout() != null) { if (getSession().getTimeout() != null) {
container.setSessionTimeout(getSession().getTimeout()); container.setSessionTimeout(getSession().getTimeout());
} }
container.setPersistSession(getSession().isPersistent());
if (getSsl() != null) { if (getSsl() != null) {
container.setSsl(getSsl()); container.setSsl(getSsl());
} }
@ -322,6 +323,11 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
*/ */
private Set<SessionTrackingMode> trackingModes; private Set<SessionTrackingMode> trackingModes;
/**
* Persist session data between restarts.
*/
private boolean persistent;
private Cookie cookie = new Cookie(); private Cookie cookie = new Cookie();
public Cookie getCookie() { public Cookie getCookie() {
@ -344,6 +350,14 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
this.trackingModes = trackingModes; this.trackingModes = trackingModes;
} }
public boolean isPersistent() {
return this.persistent;
}
public void setPersistent(boolean persistent) {
this.persistent = persistent;
}
public static class Cookie { public static class Cookie {
/** /**

@ -78,6 +78,7 @@ content into your application; rather pick only the properties that you need.
server.jsp-servlet.registered=true # Whether or not the JSP servlet is registered server.jsp-servlet.registered=true # Whether or not the JSP servlet is registered
server.servlet-path= # the servlet path, defaults to '/' server.servlet-path= # the servlet path, defaults to '/'
server.display-name= # the display name of the application server.display-name= # the display name of the application
server.session.persistent= # true if session should be saved across restarts
server.session.timeout= # session timeout in seconds server.session.timeout= # session timeout in seconds
server.session.tracking-modes= # tracking modes (one or more of "cookie" ,"url", "ssl") server.session.tracking-modes= # tracking modes (one or more of "cookie" ,"url", "ssl")
server.session.cookie.name= # session cookie name server.session.cookie.name= # session cookie name

@ -1581,7 +1581,7 @@ Common server settings include:
* `server.port` -- The listen port for incoming HTTP requests. * `server.port` -- The listen port for incoming HTTP requests.
* `server.address` -- The interface address to bind to. * `server.address` -- The interface address to bind to.
* `server.sessionTimeout` -- A session timeout. * `server.session.timeout` -- A session timeout.
See the {sc-spring-boot-autoconfigure}/web/ServerProperties.{sc-ext}[`ServerProperties`] See the {sc-spring-boot-autoconfigure}/web/ServerProperties.{sc-ext}[`ServerProperties`]
class for a complete list. class for a complete list.

@ -64,6 +64,8 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
private int sessionTimeout = DEFAULT_SESSION_TIMEOUT; private int sessionTimeout = DEFAULT_SESSION_TIMEOUT;
private boolean persistSession;
private Ssl ssl; private Ssl ssl;
private JspServlet jspServlet = new JspServlet(); private JspServlet jspServlet = new JspServlet();
@ -178,6 +180,15 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
return this.sessionTimeout; return this.sessionTimeout;
} }
@Override
public void setPersistSession(boolean persistSession) {
this.persistSession = persistSession;
}
public boolean isPersistSession() {
return this.persistSession;
}
@Override @Override
public void setInitializers(List<? extends ServletContextInitializer> initializers) { public void setInitializers(List<? extends ServletContextInitializer> initializers) {
Assert.notNull(initializers, "Initializers must not be null"); Assert.notNull(initializers, "Initializers must not be null");

@ -73,6 +73,12 @@ public interface ConfigurableEmbeddedServletContainer {
*/ */
void setSessionTimeout(int sessionTimeout, TimeUnit timeUnit); void setSessionTimeout(int sessionTimeout, TimeUnit timeUnit);
/**
* Sets if session data should be persisted between restarts.
* @param persistSession {@code true} if session data should be persisted
*/
void setPersistSession(boolean persistSession);
/** /**
* Sets the specific network address that the server should bind to. * Sets the specific network address that the server should bind to.
* @param address the address to set (defaults to {@code null}) * @param address the address to set (defaults to {@code null})

@ -40,6 +40,7 @@ import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.servlet.ServletMapping;
@ -50,6 +51,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.AbstractConfiguration; import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.boot.ApplicationTemp;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.Compression; import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
@ -277,10 +279,29 @@ public class JettyEmbeddedServletContainerFactory extends
Configuration[] configurations = getWebAppContextConfigurations(context, Configuration[] configurations = getWebAppContextConfigurations(context,
initializersToUse); initializersToUse);
context.setConfigurations(configurations); context.setConfigurations(configurations);
int sessionTimeout = (getSessionTimeout() > 0 ? getSessionTimeout() : -1); configureSession(context);
postProcessWebAppContext(context);
}
private void configureSession(WebAppContext context) {
SessionManager sessionManager = context.getSessionHandler().getSessionManager(); SessionManager sessionManager = context.getSessionHandler().getSessionManager();
int sessionTimeout = (getSessionTimeout() > 0 ? getSessionTimeout() : -1);
sessionManager.setMaxInactiveInterval(sessionTimeout); sessionManager.setMaxInactiveInterval(sessionTimeout);
postProcessWebAppContext(context); if (isPersistSession()) {
Assert.isInstanceOf(HashSessionManager.class, sessionManager,
"Unable to use persistent sessions");
configurePersistSession(sessionManager);
}
}
private void configurePersistSession(SessionManager sessionManager) {
try {
File storeDirectory = new ApplicationTemp().getFolder("jetty-sessions");
((HashSessionManager) sessionManager).setStoreDirectory(storeDirectory);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
} }
private File getTempDirectory() { private File getTempDirectory() {

@ -39,10 +39,12 @@ import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle; import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Manager;
import org.apache.catalina.Valve; import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper; import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Connector;
import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener; import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.coyote.AbstractProtocol; import org.apache.coyote.AbstractProtocol;
@ -50,6 +52,7 @@ import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11JsseProtocol; import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.boot.ApplicationTemp;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.Compression; import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
@ -379,15 +382,36 @@ public class TomcatEmbeddedServletContainerFactory extends
for (MimeMappings.Mapping mapping : getMimeMappings()) { for (MimeMappings.Mapping mapping : getMimeMappings()) {
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType()); context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
} }
configureSession(context);
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
}
private void configureSession(Context context) {
long sessionTimeout = getSessionTimeoutInMinutes();
context.setSessionTimeout((int) sessionTimeout);
if (isPersistSession()) {
Manager manager = context.getManager();
if (manager == null) {
manager = new StandardManager();
context.setManager(manager);
}
Assert.state(manager instanceof StandardManager,
"Unable to persist HTTP session state using manager type "
+ manager.getClass().getName());
File folder = new ApplicationTemp().getFolder("tomcat-sessions");
File file = new File(folder, "SESSIONS.ser");
((StandardManager) manager).setPathname(file.getAbsolutePath());
}
}
private long getSessionTimeoutInMinutes() {
long sessionTimeout = getSessionTimeout(); long sessionTimeout = getSessionTimeout();
if (sessionTimeout > 0) { if (sessionTimeout > 0) {
// Tomcat timeouts are in minutes
sessionTimeout = Math.max(TimeUnit.SECONDS.toMinutes(sessionTimeout), 1L); sessionTimeout = Math.max(TimeUnit.SECONDS.toMinutes(sessionTimeout), 1L);
} }
context.setSessionTimeout((int) sessionTimeout); return sessionTimeout;
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
} }
/** /**

@ -0,0 +1,156 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* 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 org.springframework.boot.context.embedded.undertow;
import io.undertow.servlet.UndertowServletLogger;
import io.undertow.servlet.api.SessionPersistenceManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* {@link SessionPersistenceManager} that stores session information in a file.
*
* @author Phillip Webb
* @since 1.3.0
*/
public class FileSessionPersistence implements SessionPersistenceManager {
private final File folder;
public FileSessionPersistence(File folder) {
this.folder = folder;
}
@Override
public void persistSessions(String deploymentName,
Map<String, PersistentSession> sessionData) {
try {
save(sessionData, getSessionFile(deploymentName));
}
catch (Exception ex) {
UndertowServletLogger.ROOT_LOGGER.failedToPersistSessions(ex);
}
}
private void save(Map<String, PersistentSession> sessionData, File file)
throws IOException {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
try {
save(sessionData, stream);
}
finally {
stream.close();
}
}
private void save(Map<String, PersistentSession> sessionData,
ObjectOutputStream stream) throws IOException {
Map<String, Serializable> session = new LinkedHashMap<String, Serializable>();
for (Map.Entry<String, PersistentSession> entry : sessionData.entrySet()) {
session.put(entry.getKey(),
new SerializablePersistentSession(entry.getValue()));
}
stream.writeObject(session);
}
@Override
public Map<String, PersistentSession> loadSessionAttributes(String deploymentName,
final ClassLoader classLoader) {
try {
File file = getSessionFile(deploymentName);
if (file.exists()) {
return load(file);
}
}
catch (Exception ex) {
UndertowServletLogger.ROOT_LOGGER.failedtoLoadPersistentSessions(ex);
}
return null;
}
private Map<String, PersistentSession> load(File file) throws IOException,
ClassNotFoundException {
ObjectInputStream stream = new ObjectInputStream(new FileInputStream(file));
try {
return load(stream);
}
finally {
stream.close();
}
}
private Map<String, PersistentSession> load(ObjectInputStream stream)
throws ClassNotFoundException, IOException {
Map<String, SerializablePersistentSession> session = readSession(stream);
long time = System.currentTimeMillis();
Map<String, PersistentSession> result = new LinkedHashMap<String, PersistentSession>();
for (Map.Entry<String, SerializablePersistentSession> entry : session.entrySet()) {
PersistentSession entrySession = entry.getValue().getPersistentSession();
if (entrySession.getExpiration().getTime() > time) {
result.put(entry.getKey(), entrySession);
}
}
return result;
}
@SuppressWarnings("unchecked")
private Map<String, SerializablePersistentSession> readSession(
ObjectInputStream stream) throws ClassNotFoundException, IOException {
return ((Map<String, SerializablePersistentSession>) stream.readObject());
}
private File getSessionFile(String deploymentName) {
if (!this.folder.exists()) {
this.folder.mkdirs();
}
return new File(this.folder, deploymentName + ".session");
}
@Override
public void clear(String deploymentName) {
getSessionFile(deploymentName).delete();
}
static class SerializablePersistentSession implements Serializable {
private static final long serialVersionUID = 0L;
private final Date expiration;
private final Map<String, Object> sessionData;
public SerializablePersistentSession(PersistentSession session) {
this.expiration = session.getExpiration();
this.sessionData = new LinkedHashMap<String, Object>(session.getSessionData());
}
public PersistentSession getPersistentSession() {
return new PersistentSession(this.expiration, this.sessionData);
}
}
}

@ -213,9 +213,15 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
@Override @Override
public synchronized void stop() throws EmbeddedServletContainerException { public synchronized void stop() throws EmbeddedServletContainerException {
if (this.started) { if (this.started) {
try {
this.started = false; this.started = false;
this.manager.stop();
this.undertow.stop(); this.undertow.stop();
} }
catch (Exception ex) {
throw new EmbeddedServletContainerException("Unable to stop undertow", ex);
}
}
} }
@Override @Override

@ -62,6 +62,7 @@ import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import org.springframework.boot.ApplicationTemp;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
@ -355,6 +356,10 @@ public class UndertowEmbeddedServletContainerFactory extends
if (isAccessLogEnabled()) { if (isAccessLogEnabled()) {
configureAccessLog(deployment); configureAccessLog(deployment);
} }
if (isPersistSession()) {
File folder = new ApplicationTemp().getFolder("undertow-sessions");
deployment.setSessionPersistenceManager(new FileSessionPersistence(folder));
}
DeploymentManager manager = Servlets.defaultContainer().addDeployment(deployment); DeploymentManager manager = Servlets.defaultContainer().addDeployment(deployment);
manager.deploy(); manager.deploy();
SessionManager sessionManager = manager.getDeployment().getSessionManager(); SessionManager sessionManager = manager.getDeployment().getSessionManager();

@ -21,6 +21,7 @@ import java.io.FileInputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -38,13 +39,17 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.InputStreamFactory; import org.apache.http.client.entity.InputStreamFactory;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContextBuilder;
import org.junit.After; import org.junit.After;
import org.junit.Rule; import org.junit.Rule;
@ -96,6 +101,8 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
protected EmbeddedServletContainer container; protected EmbeddedServletContainer container;
private final HttpClientContext httpClientContext = HttpClientContext.create();
@After @After
public void teardown() { public void teardown() {
if (this.container != null) { if (this.container != null) {
@ -529,6 +536,28 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
assertThat(getFactory().getSessionTimeout(), equalTo(30 * 60)); assertThat(getFactory().getSessionTimeout(), equalTo(30 * 60));
} }
@Test
public void persistSession() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
factory.setPersistSession(true);
this.container = factory
.getEmbeddedServletContainer(sessionServletRegistration());
this.container.start();
String s1 = getResponse(getLocalUrl("/session"));
String s2 = getResponse(getLocalUrl("/session"));
this.container.stop();
this.container = factory
.getEmbeddedServletContainer(sessionServletRegistration());
this.container.start();
String s3 = getResponse(getLocalUrl("/session"));
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
String message = "Session error s1=" + s1 + " s2=" + s2 + " s3=" + s3;
assertThat(message, s2.split(":")[0], equalTo(s1.split(":")[1]));
assertThat(message, s3.split(":")[0], equalTo(s2.split(":")[1]));
}
@Test @Test
public void compression() throws Exception { public void compression() throws Exception {
assertTrue(doTestCompression(10000, null, null)); assertTrue(doTestCompression(10000, null, null));
@ -632,7 +661,14 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
protected ClientHttpResponse getClientResponse(String url) throws IOException, protected ClientHttpResponse getClientResponse(String url) throws IOException,
URISyntaxException { URISyntaxException {
return getClientResponse(url, new HttpComponentsClientHttpRequestFactory()); return getClientResponse(url, new HttpComponentsClientHttpRequestFactory() {
@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext;
}
});
} }
protected ClientHttpResponse getClientResponse(String url, protected ClientHttpResponse getClientResponse(String url,
@ -640,6 +676,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
URISyntaxException { URISyntaxException {
ClientHttpRequest request = requestFactory.createRequest(new URI(url), ClientHttpRequest request = requestFactory.createRequest(new URI(url),
HttpMethod.GET); HttpMethod.GET);
request.getHeaders().add("Cookie", "JSESSIONID=" + "123");
ClientHttpResponse response = request.execute(); ClientHttpResponse response = request.execute();
return response; return response;
} }
@ -655,16 +692,37 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@SuppressWarnings("serial") @SuppressWarnings("serial")
private ServletContextInitializer errorServletRegistration() { private ServletContextInitializer errorServletRegistration() {
ServletRegistrationBean bean = new ServletRegistrationBean(new ExampleServlet() { ServletRegistrationBean bean = new ServletRegistrationBean(new ExampleServlet() {
@Override @Override
public void service(ServletRequest request, ServletResponse response) public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
throw new RuntimeException("Planned"); throw new RuntimeException("Planned");
} }
}, "/bang"); }, "/bang");
bean.setName("error"); bean.setName("error");
return bean; return bean;
} }
private ServletContextInitializer sessionServletRegistration() {
ServletRegistrationBean bean = new ServletRegistrationBean(new ExampleServlet() {
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
HttpSession session = ((HttpServletRequest) request).getSession(true);
long value = System.currentTimeMillis();
Object existing = session.getAttribute("boot");
session.setAttribute("boot", value);
PrintWriter writer = response.getWriter();
writer.append(String.valueOf(existing) + ":" + value);
}
}, "/session");
bean.setName("session");
return bean;
}
private class TestGzipInputStreamFactory implements InputStreamFactory { private class TestGzipInputStreamFactory implements InputStreamFactory {
private final AtomicBoolean requested = new AtomicBoolean(false); private final AtomicBoolean requested = new AtomicBoolean(false);

@ -0,0 +1,109 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* 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 org.springframework.boot.context.embedded.undertow;
import io.undertow.servlet.api.SessionPersistenceManager.PersistentSession;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link FileSessionPersistence}.
*
* @author Phillip Webb
*/
public class FileSessionPersistenceTests {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
private File folder;
private FileSessionPersistence persistence;
private ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
private Date expiration = new Date(System.currentTimeMillis() + 10000);
@Before
public void setup() throws IOException {
this.folder = this.temp.newFolder();
this.persistence = new FileSessionPersistence(this.folder);
}
@Test
public void loadsNullForMissingFile() throws Exception {
Map<String, PersistentSession> attributes = this.persistence
.loadSessionAttributes("test", this.classLoader);
assertThat(attributes, nullValue());
}
@Test
public void presistAndLoad() throws Exception {
Map<String, PersistentSession> sessionData = new LinkedHashMap<String, PersistentSession>();
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("spring", "boot");
PersistentSession session = new PersistentSession(this.expiration, data);
sessionData.put("abc", session);
this.persistence.persistSessions("test", sessionData);
Map<String, PersistentSession> restored = this.persistence.loadSessionAttributes(
"test", this.classLoader);
assertThat(restored, notNullValue());
assertThat(restored.get("abc").getExpiration(), equalTo(this.expiration));
assertThat(restored.get("abc").getSessionData().get("spring"),
equalTo((Object) "boot"));
}
@Test
public void dontRestoreExpired() throws Exception {
Date expired = new Date(System.currentTimeMillis() - 1000);
Map<String, PersistentSession> sessionData = new LinkedHashMap<String, PersistentSession>();
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("spring", "boot");
PersistentSession session = new PersistentSession(expired, data);
sessionData.put("abc", session);
this.persistence.persistSessions("test", sessionData);
Map<String, PersistentSession> restored = this.persistence.loadSessionAttributes(
"test", this.classLoader);
assertThat(restored, notNullValue());
assertThat(restored.containsKey("abc"), equalTo(false));
}
@Test
public void deleteFileOnClear() throws Exception {
File sessionFile = new File(this.folder, "test.session");
Map<String, PersistentSession> sessionData = new LinkedHashMap<String, PersistentSession>();
this.persistence.persistSessions("test", sessionData);
assertThat(sessionFile.exists(), equalTo(true));
this.persistence.clear("test");
assertThat(sessionFile.exists(), equalTo(false));
}
}
Loading…
Cancel
Save