Add EnableSpringHttpSession

Fixes gh-231
This commit is contained in:
Rob Winch
2015-08-18 10:11:35 -05:00
parent db45698e25
commit 16b65973b7
7 changed files with 275 additions and 44 deletions

View File

@@ -321,6 +321,22 @@ It is the responsibility of the developer to ensure the attribute is populated s
Some `SessionRepository` implementations may choose to implement `FindByUsernameSessionRepository` also.
For example, Spring's Redis support implements `FindByUsernameSessionRepository`.
[[api-enablespringhttpsession]]
=== EnableSpringHttpSession
The `@EnableSpringHttpSession` annotation can be added to an `@Configuration` class to expose the `SessionRepositoryFilter` as a bean named "springSessionRepositoryFilter".
In order to leverage the annotation, a single `SessionRepository` bean must be provided.
For example:
[source,java,indent=0]
----
include::{docs-test-dir}docs/SpringHttpSessionConfig.java[tags=class]
----
It is important to note that no infrastructure for session expirations is configured for you out of the box.
This is because things like session expiration are highly implementation dependent.
This means if you require cleaning up expired sessions, you are responsible for cleaning up the expired sessions.
[[api-redisoperationssessionrepository]]
=== RedisOperationsSessionRepository

View File

@@ -15,13 +15,20 @@
*/
package docs;
import org.junit.Test;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.*;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.mock.web.MockServletContext;
import org.springframework.session.ExpiringSession;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
/**
* @author Rob Winch
*/
@@ -104,6 +111,20 @@ public class IndexDocTests {
// end::new-mapsessionrepository[]
}
@Test
public void runSpringHttpSessionConfig() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringHttpSessionConfig.class);
context.setServletContext(new MockServletContext());
context.refresh();
try {
context.getBean(SessionRepositoryFilter.class);
} finally {
context.close();
}
}
private static class User {
private User(String username) {}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-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 docs;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
// tag::class[]
@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
@Bean
public MapSessionRepository sessionRepository() {
return new MapSessionRepository();
}
}
// end::class[]

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-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.session.config.annotation.web.http;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.session.SessionRepository;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
/**
* Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and
* backed by a user provided implementation of {@link SessionRepository}. In
* order to leverage the annotation, a single {@link SessionRepository} bean
* must be provided. For example:
*
* <pre>
* <code>
* {@literal @Configuration}
* {@literal @EnableSpringHttpSession}
* public class SpringHttpSessionConfig {
*
* {@literal @Bean}
* public MapSessionRepository sessionRepository() {
* return new MapSessionRepository();
* }
*
* }
* </code>
* </pre>
*
* <p>
* It is important to note that no infrastructure for session expirations is
* configured for you out of the box. This is because things like session
* expiration are highly implementation dependent. This means if you require
* cleaning up expired sessions, you are responsible for cleaning up the expired
* sessions.
* </p>
*
* <p>
* The following is provided for you with the base configuration:
* </p>
*
* <ul>
* <li>SessionRepositoryFilter - is responsible for wrapping the
* HttpServletRequest with an implementation of HttpSession that is backed by a
* SessionRepository</li>
* <li>SessionEventHttpSessionListenerAdapter - is responsible for translating
* Spring Session events into HttpSessionEvent. In order for it to work, the
* implementation of SessionRepository you provide must support
* {@link SessionCreatedEvent} and {@link SessionDestroyedEvent}.</li>
* <li>
* </ul>
*
* @author Rob Winch
* @since 1.0
*/
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(SpringHttpSessionConfiguration.class)
@Configuration
public @interface EnableSpringHttpSession {
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2002-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.session.config.annotation.web.http;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.session.web.http.HttpSessionStrategy;
import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter;
import org.springframework.session.web.http.SessionRepositoryFilter;
/**
* Configures the basics for setting up Spring Session in a web environment. In
* order to use it, you must provide a {@link SessionRepository}. For example:
*
* <pre>
* {@literal @Configuration}
* {@literal @EnableSpringHttpSession}
* public class SpringHttpSessionConfig {
*
* {@literal @Bean}
* public MapSessionRepository sessionRepository() {
* return new MapSessionRepository();
* }
*
* }
* </pre>
*
* <p>
* It is important to note that no infrastructure for session expirations is
* configured for you out of the box. This is because things like session
* expiration are highly implementation dependent. This means if you require
* cleaning up expired sessions, you are responsible for cleaning up the expired
* sessions.
* </p>
*
* <p>
* The following is provided for you with the base configuration:
* </p>
*
* <ul>
* <li>SessionRepositoryFilter - is responsible for wrapping the
* HttpServletRequest with an implementation of HttpSession that is backed by a
* SessionRepository</li>
* <li>SessionEventHttpSessionListenerAdapter - is responsible for translating
* Spring Session events into HttpSessionEvent. In order for it to work, the
* implementation of SessionRepository you provide must support
* {@link SessionCreatedEvent} and {@link SessionDestroyedEvent}.</li>
* <li>
* </ul>
*
* @author Rob Winch
* @since 1.1
*
* @see EnableSpringHttpSession
*/
@Configuration
@EnableScheduling
public class SpringHttpSessionConfiguration {
private HttpSessionStrategy httpSessionStrategy;
private List<HttpSessionListener> httpSessionListeners = new ArrayList<HttpSessionListener>();
@Bean
public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
return new SessionEventHttpSessionListenerAdapter(httpSessionListeners);
}
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(SessionRepository<S> sessionRepository, ServletContext servletContext) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(sessionRepository);
sessionRepositoryFilter.setServletContext(servletContext);
if(httpSessionStrategy != null) {
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
}
return sessionRepositoryFilter;
}
@Autowired(required = false)
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
this.httpSessionStrategy = httpSessionStrategy;
}
@Autowired(required = false)
public void setHttpSessionListeners(List<HttpSessionListener> listeners) {
this.httpSessionListeners = listeners;
}
}

View File

@@ -22,6 +22,7 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
/**
* Add this annotation to an {@code @Configuration} class to expose the
@@ -29,6 +30,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
* backed by Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory}
* must be provided. For example:
* <pre>
* <code>
* {@literal @Configuration}
* {@literal @EnableRedisHttpSession}
* public class RedisHttpSessionConfig {
@@ -39,12 +41,14 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
* }
*
* }
* </code>
* </pre>
*
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} instead.
*
* @author Rob Winch
* @since 1.0
* @see EnableSpringHttpSession
*/
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value={java.lang.annotation.ElementType.TYPE})

View File

@@ -15,14 +15,9 @@
*/
package org.springframework.session.data.redis.config.annotation.web.http;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionListener;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
@@ -42,13 +37,10 @@ import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.session.ExpiringSession;
import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.data.redis.RedisOperationsSessionRepository;
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.web.http.HttpSessionStrategy;
import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -65,25 +57,16 @@ import org.springframework.util.StringUtils;
*/
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoaderAware {
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private Integer maxInactiveIntervalInSeconds = 1800;
private HttpSessionStrategy httpSessionStrategy;
private ConfigureRedisAction configureRedisAction = new ConfigureNotifyKeyspaceEventsAction();
private List<HttpSessionListener> httpSessionListeners = new ArrayList<HttpSessionListener>();
private String redisNamespace = "";
@Bean
public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
return new SessionEventHttpSessionListenerAdapter(httpSessionListeners);
}
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory, RedisOperationsSessionRepository messageListener) {
@@ -118,16 +101,6 @@ public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoad
return sessionRepository;
}
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(SessionRepository<S> sessionRepository, ServletContext servletContext) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(sessionRepository);
sessionRepositoryFilter.setServletContext(servletContext);
if(httpSessionStrategy != null) {
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
}
return sessionRepositoryFilter;
}
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
@@ -164,16 +137,6 @@ public class RedisHttpSessionConfiguration implements ImportAware, BeanClassLoad
this.redisNamespace = enableAttrs.getString("redisNamespace");
}
@Autowired(required = false)
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
this.httpSessionStrategy = httpSessionStrategy;
}
@Autowired(required = false)
public void setHttpSessionListeners(List<HttpSessionListener> listeners) {
this.httpSessionListeners = listeners;
}
@Bean
public InitializingBean enableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory) {
return new EnableRedisKeyspaceNotificationsInitializer(connectionFactory, configureRedisAction);