Add server.session.store-dir support
Add support for a `server.session.store-dir` property which can be used to specify where session data source be saved. Fixes gh-4191
This commit is contained in:
@@ -162,6 +162,7 @@ public class ServerProperties
|
||||
container.setSessionTimeout(getSession().getTimeout());
|
||||
}
|
||||
container.setPersistSession(getSession().isPersistent());
|
||||
container.setSessionStoreDir(getSession().getStoreDir());
|
||||
if (getSsl() != null) {
|
||||
container.setSsl(getSsl());
|
||||
}
|
||||
@@ -392,6 +393,11 @@ public class ServerProperties
|
||||
*/
|
||||
private boolean persistent;
|
||||
|
||||
/**
|
||||
* The directory used to store session data.
|
||||
*/
|
||||
private File storeDir;
|
||||
|
||||
private Cookie cookie = new Cookie();
|
||||
|
||||
public Cookie getCookie() {
|
||||
@@ -422,6 +428,14 @@ public class ServerProperties
|
||||
this.persistent = persistent;
|
||||
}
|
||||
|
||||
public File getStoreDir() {
|
||||
return this.storeDir;
|
||||
}
|
||||
|
||||
public void setStoreDir(File storeDir) {
|
||||
this.storeDir = storeDir;
|
||||
}
|
||||
|
||||
public static class Cookie {
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
@@ -384,6 +385,17 @@ public class ServerPropertiesTests {
|
||||
verify(container).setUseForwardHeaders(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionStoreDir() throws Exception {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("server.session.store-dir", "myfolder");
|
||||
bindProperties(map);
|
||||
JettyEmbeddedServletContainerFactory container = spy(
|
||||
new JettyEmbeddedServletContainerFactory());
|
||||
this.properties.customize(container);
|
||||
verify(container).setSessionStoreDir(new File("myfolder"));
|
||||
}
|
||||
|
||||
private void bindProperties(Map<String, String> map) {
|
||||
new RelaxedDataBinder(this.properties, "server")
|
||||
.bind(new MutablePropertyValues(map));
|
||||
|
||||
@@ -87,6 +87,7 @@ content into your application; rather pick only the properties that you need.
|
||||
server.display-name= # the display name of the application
|
||||
server.use-forward-headers= # if X-Forwarded-* headers should be used (default is off unless running in a known cloud)
|
||||
server.session.persistent=false # true if session should be saved across restarts
|
||||
server.session.store-dir= # dir used to save session data
|
||||
server.session.timeout= # session timeout in seconds
|
||||
server.session.tracking-modes= # tracking modes (one or more of "cookie" ,"url", "ssl")
|
||||
server.session.cookie.name= # session cookie name
|
||||
|
||||
@@ -3584,7 +3584,7 @@ Spring Session provides support for managing a user's session information. If yo
|
||||
writing a web application and Spring Session and Spring Data Redis are both on the
|
||||
classpath, Spring Boot will auto-configure Spring Session through its
|
||||
`@EnableRedisHttpSession`. Session data will be stored in Redis and the session timeout
|
||||
can be configured using the `server.session-timeout` property.
|
||||
can be configured using the `server.session.timeout` property.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ public abstract class AbstractConfigurableEmbeddedServletContainer
|
||||
|
||||
private boolean persistSession;
|
||||
|
||||
private File sessionStoreDir;
|
||||
|
||||
private Ssl ssl;
|
||||
|
||||
private JspServlet jspServlet = new JspServlet();
|
||||
@@ -191,6 +193,15 @@ public abstract class AbstractConfigurableEmbeddedServletContainer
|
||||
return this.persistSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionStoreDir(File sessionStoreDir) {
|
||||
this.sessionStoreDir = sessionStoreDir;
|
||||
}
|
||||
|
||||
public File getSessionStoreDir() {
|
||||
return this.sessionStoreDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitializers(List<? extends ServletContextInitializer> initializers) {
|
||||
Assert.notNull(initializers, "Initializers must not be null");
|
||||
|
||||
@@ -26,6 +26,9 @@ import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.ApplicationHome;
|
||||
import org.springframework.boot.ApplicationTemp;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link EmbeddedServletContainerFactory} implementations.
|
||||
@@ -140,4 +143,24 @@ public abstract class AbstractEmbeddedServletContainerFactory
|
||||
}
|
||||
}
|
||||
|
||||
protected final File getValidSessionStoreDir() {
|
||||
return getValidSessionStoreDir(true);
|
||||
}
|
||||
|
||||
protected final File getValidSessionStoreDir(boolean mkdirs) {
|
||||
File dir = getSessionStoreDir();
|
||||
if (dir == null) {
|
||||
return new ApplicationTemp().getFolder("servlet-sessions");
|
||||
}
|
||||
if (!dir.isAbsolute()) {
|
||||
dir = new File(new ApplicationHome().getDir(), dir.getPath());
|
||||
}
|
||||
if (!dir.exists() && mkdirs) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
Assert.state(!mkdirs || dir.exists(), "Session dir " + dir + " does not exist");
|
||||
Assert.state(!dir.isFile(), "Session dir " + dir + " points to a file");
|
||||
return dir;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,12 @@ public interface ConfigurableEmbeddedServletContainer {
|
||||
*/
|
||||
void setPersistSession(boolean persistSession);
|
||||
|
||||
/**
|
||||
* Set the directory used to store serialized session data.
|
||||
* @param sessionStoreDir the directory or {@code null} to use a default location.
|
||||
*/
|
||||
void setSessionStoreDir(File sessionStoreDir);
|
||||
|
||||
/**
|
||||
* Sets the specific network address that the server should bind to.
|
||||
* @param address the address to set (defaults to {@code null})
|
||||
|
||||
@@ -53,7 +53,6 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.springframework.boot.ApplicationTemp;
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.Compression;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
@@ -304,8 +303,8 @@ public class JettyEmbeddedServletContainerFactory
|
||||
|
||||
private void configurePersistSession(SessionManager sessionManager) {
|
||||
try {
|
||||
File storeDirectory = new ApplicationTemp().getFolder("jetty-sessions");
|
||||
((HashSessionManager) sessionManager).setStoreDirectory(storeDirectory);
|
||||
((HashSessionManager) sessionManager)
|
||||
.setStoreDirectory(getValidSessionStoreDir());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.apache.coyote.ProtocolHandler;
|
||||
import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
|
||||
import org.apache.coyote.http11.AbstractHttp11Protocol;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.ApplicationTemp;
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.Compression;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
@@ -411,7 +410,7 @@ public class TomcatEmbeddedServletContainerFactory
|
||||
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 folder = getValidSessionStoreDir();
|
||||
File file = new File(folder, "SESSIONS.ser");
|
||||
((StandardManager) manager).setPathname(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.boot.ApplicationTemp;
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
|
||||
@@ -360,7 +359,7 @@ public class UndertowEmbeddedServletContainerFactory
|
||||
configureAccessLog(deployment);
|
||||
}
|
||||
if (isPersistSession()) {
|
||||
File folder = new ApplicationTemp().getFolder("undertow-sessions");
|
||||
File folder = getValidSessionStoreDir();
|
||||
deployment.setSessionPersistenceManager(new FileSessionPersistence(folder));
|
||||
}
|
||||
DeploymentManager manager = Servlets.defaultContainer().addDeployment(deployment);
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.context.embedded;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
@@ -58,6 +59,8 @@ import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.mockito.InOrder;
|
||||
import org.springframework.boot.ApplicationHome;
|
||||
import org.springframework.boot.ApplicationTemp;
|
||||
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -72,6 +75,7 @@ import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
@@ -564,6 +568,54 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||
assertThat(message, s3.split(":")[0], equalTo(s2.split(":")[1]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistSessionInSpecificSessionStoreDir() throws Exception {
|
||||
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||
File sessionStoreDir = this.temporaryFolder.newFolder();
|
||||
factory.setPersistSession(true);
|
||||
factory.setSessionStoreDir(sessionStoreDir);
|
||||
this.container = factory
|
||||
.getEmbeddedServletContainer(sessionServletRegistration());
|
||||
this.container.start();
|
||||
getResponse(getLocalUrl("/session"));
|
||||
this.container.stop();
|
||||
File[] dirContents = sessionStoreDir.listFiles(new FilenameFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return !(".".equals(name) || "..".equals(name));
|
||||
}
|
||||
|
||||
});
|
||||
assertThat(dirContents.length, greaterThan(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValidSessionStoreWhenSessionStoreNotSet() throws Exception {
|
||||
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||
File dir = factory.getValidSessionStoreDir(false);
|
||||
assertThat(dir.getName(), equalTo("servlet-sessions"));
|
||||
assertThat(dir.getParentFile(), equalTo(new ApplicationTemp().getFolder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValidSessionStoreWhenSessionStoreIsRelative() throws Exception {
|
||||
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||
factory.setSessionStoreDir(new File("sessions"));
|
||||
File dir = factory.getValidSessionStoreDir(false);
|
||||
assertThat(dir.getName(), equalTo("sessions"));
|
||||
assertThat(dir.getParentFile(), equalTo(new ApplicationHome().getDir()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValidSessionStoreWhenSessionStoreReferencesFile() throws Exception {
|
||||
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||
factory.setSessionStoreDir(this.temporaryFolder.newFile());
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("points to a file");
|
||||
factory.getValidSessionStoreDir(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compression() throws Exception {
|
||||
assertTrue(doTestCompression(10000, null, null));
|
||||
|
||||
Reference in New Issue
Block a user