Compare commits

...

12 Commits

Author SHA1 Message Date
Spring Buildmaster
03f008f283 Release version 1.2.0.RC3 2016-04-27 01:18:46 +00:00
Vedran Pavic
00e7110594 Implement individual attribute persistence in JdbcOperationsSessionRepository 2016-04-26 14:29:23 -05:00
Eddú Meléndez Gonzales
f973e63fce Set collectionName attribute in MongoOperationsSessionRepository
Previous to this commit, collectionName could be set in
MongoHttpSessionConfiguration but it was never used. Now, attribute
can be set into MongoOperationsSessionRepository to take effect.

See gh-489
2016-04-26 14:27:02 -05:00
Eddú Meléndez
edbf8bf587 Expose attributes in MongoHttpSessionConfiguration
Attributes collectionName and maxInactiveIntervalInSeconds can be set
now.

Fixes gh-489
2016-04-20 13:49:18 -05:00
Johnny Lim
234f6c954a Remove duplicate words
Remove duplicate words

Fixes gh-494
2016-04-18 23:27:48 -05:00
Rob Winch
e0417523f6 Add tableName setter
Add tableName setter for JdbcHttpSessionConfiguration
2016-04-18 23:18:58 -05:00
Eddú Meléndez
5865b0a715 Add tableName setter for JdbcHttpSessionConfiguration
See gh-488
2016-04-19 01:00:40 +10:00
Vedran Pavić
67201417df Add compile dependency to commons-logging (#476) 2016-04-11 10:55:10 -05:00
Vedran Pavić
01a149737e Add GitHub issue/PR tempaltes
Add
2016-04-11 10:54:13 -05:00
John Blum
2d6f505a30 Update to Spring Data Hopper
Fixes gh-470
2016-04-11 09:31:05 -05:00
Rob Winch
7c616a1adf Redis save does nothing if nothing has changed
Fixes gh-467
2016-04-06 15:38:34 -05:00
Spring Buildmaster
61b01d9ecd Next development version 2016-04-06 14:26:42 +00:00
43 changed files with 841 additions and 332 deletions

3
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,3 @@
<!--
Thanks for raising a Spring Session issue. Please provide a brief description of your problem along with the version of Spring Session that you are using. If possible, please also consider putting together a sample application that reproduces the issue.
-->

6
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,6 @@
<!--
Thanks for contributing to Spring Session. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
-->
<!-- Please also confirm that you have signed the CLA by put an [X] in the box below: -->
- [] I have signed the CLA

View File

@@ -160,7 +160,7 @@ Specifically, we notice the following things about our response:
* We have a header with the name of *x-auth-token* which contains a new session id
* The current username is displayed
We can now use the *x-auth-token* to make another request without providing the username and password again. For example, the following outputs the the username just as before:
We can now use the *x-auth-token* to make another request without providing the username and password again. For example, the following outputs the username just as before:
$ curl -v http://localhost:8080/ -H "x-auth-token: 0dc1f6e1-c7f1-41ac-8ce2-32b6b3e57aa3"

View File

@@ -1037,7 +1037,8 @@ However, you can override the default `ConversionService` by providing a Bean na
[[api-jdbcoperationssessionrepository-storage]]
==== Storage Details
By default, this implementation uses `SPRING_SESSION` table to store sessions. Note that the table name can be easily customized as already described.
By default, this implementation uses `SPRING_SESSION` and `SPRING_SESSION_ATTRIBUTES` tables to store sessions.
Note that the table name can be easily customized as already described. In that case the table used to store attributes will be named using the provided table name, suffixed with `_ATTRIBUTES`.
Due to the differences between the various database vendors, especially when it comes to storing binary data, make sure to use SQL script specific to your database.
Scripts for most major database vendors are packaged as `org/springframework/session/jdbc/schema-\*.sql`, where `*` is the target database type.

View File

@@ -115,9 +115,7 @@ public class HttpSessionGemFireIndexingITests extends AbstractGemFireIntegration
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -50,9 +50,7 @@ public class GemFireHttpSessionConfig {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -4,8 +4,9 @@ jacksonVersion=2.6.5
jspApiVersion=2.0
servletApiVersion=3.0.1
jstlelVersion=1.2.5
version=1.2.0.RC2
springDataRedisVersion=1.6.2.RELEASE
version=1.2.0.RC3
springDataRedisVersion=1.7.1.RELEASE
commonsLoggingVersion=1.2
junitVersion=4.12
gebVersion=0.13.1
mockitoVersion=1.10.19
@@ -14,11 +15,11 @@ seleniumVersion=2.52.0
springSecurityVersion=4.0.3.RELEASE
springVersion=4.2.5.RELEASE
httpClientVersion=4.5.1
jedisVersion=2.7.3
jedisVersion=2.8.1
h2Version=1.4.191
springDataMongoVersion=1.8.2.RELEASE
springDataMongoVersion=1.9.1.RELEASE
springShellVersion=1.1.0.RELEASE
springDataGemFireVersion=1.7.4.RELEASE
springDataGemFireVersion=1.8.1.RELEASE
assertjVersion=2.3.0
spockVersion=1.0-groovy-2.4
jstlVersion=1.2.1

View File

@@ -27,8 +27,8 @@ import org.springframework.context.annotation.ImportResource;
public class Application {
public static void main(final String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
Application.class);
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Application.class);
context.registerShutdownHook();
}
}

View File

@@ -51,35 +51,32 @@ public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcess
// tag::class[]
static {
ClientMembership
.registerClientMembershipListener(new ClientMembershipListenerAdapter() {
public void memberJoined(final ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
public void memberJoined(final ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
});
}
});
}
@SuppressWarnings("all")
@Resource(name = "applicationProperties")
private Properties applicationProperties;
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
String host = getServerHost(DEFAULT_SERVER_HOST);
Assert.isTrue(waitForCacheServerToStart(host, this.port),
String.format(
"GemFire Server failed to start [host: '%1$s', port: %2$d]%n",
host, this.port));
String.format("GemFire Server failed to start [host: '%1$s', port: %2$d]%n",
host, this.port));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
try {
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
@@ -157,4 +154,5 @@ public class GemFireCacheServerReadyBeanPostProcessor implements BeanPostProcess
return condition.evaluate();
}
}

View File

@@ -35,6 +35,8 @@
<prop key="log-level">${sample.httpsession.gemfire.log-level:warning}</prop>
</util:properties>
<gfe:client-cache properties-ref="gemfireProperties"/>
<!--7-->
<gfe:pool free-connection-timeout="5000"
keep-alive="false"
@@ -47,9 +49,6 @@
<gfe:server host="${application.gemfire.client-server.host}"
port="${spring.session.data.gemfire.port:${application.gemfire.client-server.port}}"/>
</gfe:pool>
<gfe:client-cache properties-ref="gemfireProperties"
use-bean-factory-locator="false"/>
<!-- end::beans[] -->
</beans>

View File

@@ -9,9 +9,7 @@
<!-- tag::context-param[] -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/session-client.xml
</param-value>
<param-value>/WEB-INF/spring/session-client.xml</param-value>
</context-param>
<!-- end::context-param[] -->
@@ -23,9 +21,9 @@
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
<!-- end::springSessionRepositoryFilter[] -->
@@ -36,9 +34,7 @@
-->
<!-- tag::listeners[] -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- end::listeners[] -->

View File

@@ -36,7 +36,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
import org.springframework.session.data.gemfire.support.GemFireUtils;
@@ -52,17 +51,20 @@ public class ClientConfig {
static final CountDownLatch latch = new CountDownLatch(1);
static {
System.setProperty("gemfire.log-level",
System.getProperty("sample.httpsession.gemfire.log-level", "warning"));
System.setProperty("gemfire.log-level", logLevel());
ClientMembership
.registerClientMembershipListener(new ClientMembershipListenerAdapter() {
public void memberJoined(ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
ClientMembership.registerClientMembershipListener(
new ClientMembershipListenerAdapter() {
public void memberJoined(ClientMembershipEvent event) {
if (!event.isClient()) {
latch.countDown();
}
});
}
});
}
private static String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
}
@Bean
@@ -75,14 +77,22 @@ public class ClientConfig {
return new Properties();
}
@Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
@Bean
ClientCacheFactoryBean gemfireCache() { // <4>
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
return clientCacheFactory;
}
@Bean
PoolFactoryBean gemfirePool(// <3>
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT
+ "}") int port) {
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setName(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMaxConnections(ServerConfig.MAX_CONNECTIONS);
@@ -92,46 +102,29 @@ public class ClientConfig {
poolFactory.setSubscriptionEnabled(true);
poolFactory.setThreadLocalConnections(false);
poolFactory.setServerEndpoints(Collections.singletonList(
new ConnectionEndpoint(ServerConfig.SERVER_HOSTNAME, port)));
poolFactory.setServers(Collections.singletonList(
new ConnectionEndpoint(ServerConfig.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
ClientCacheFactoryBean gemfireCache(Pool gemfirePool) { // <4>
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
clientCacheFactory.setPool(gemfirePool);
clientCacheFactory.setUseBeanFactoryLocator(false);
return clientCacheFactory;
}
@Bean
BeanPostProcessor gemfireCacheServerReadyBeanPostProcessor(// <5>
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT
+ "}") final int port) {
@Value("${spring.session.data.gemfire.port:" + ServerConfig.SERVER_PORT + "}") final int port) {
return new BeanPostProcessor() {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
Assert.isTrue(
waitForCacheServerToStart(ServerConfig.SERVER_HOSTNAME, port),
String.format(
"GemFire Server failed to start [hostname: %1$s, port: %2$d]",
ServerConfig.SERVER_HOSTNAME, port));
Assert.isTrue(waitForCacheServerToStart(ServerConfig.SERVER_HOSTNAME, port),
String.format("GemFire Server failed to start [hostname: %1$s, port: %2$d]",
ServerConfig.SERVER_HOSTNAME, port));
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof PoolFactoryBean || bean instanceof Pool) {
try {
latch.await(DEFAULT_WAIT_DURATION, TimeUnit.MILLISECONDS);
@@ -206,4 +199,5 @@ public class ClientConfig {
return condition.evaluate();
}
}

View File

@@ -49,20 +49,23 @@ public class ServerConfig {
gemfireProperties.setProperty("name", "GemFireClientServerHttpSessionSample");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level",
System.getProperty("sample.httpsession.gemfire.log-level", "warning"));
gemfireProperties.setProperty("log-level", logLevel());
gemfireProperties.setProperty("jmx-manager", "true");
gemfireProperties.setProperty("jmx-manager-start", "true");
return gemfireProperties;
}
private String logLevel() {
return System.getProperty("sample.httpsession.gemfire.log-level", "warning");
}
@Bean
CacheFactoryBean gemfireCache() { // <3>
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}
@@ -76,6 +79,7 @@ public class ServerConfig {
cacheServerFactory.setAutoStartup(true);
cacheServerFactory.setBindAddress(SERVER_HOSTNAME);
cacheServerFactory.setCache(gemfireCache);
cacheServerFactory.setHostNameForClients(SERVER_HOSTNAME);
cacheServerFactory.setMaxConnections(MAX_CONNECTIONS);
cacheServerFactory.setPort(port);
@@ -86,5 +90,6 @@ public class ServerConfig {
public static void main(final String[] args) throws IOException { // <5>
new AnnotationConfigApplicationContext(ServerConfig.class).registerShutdownHook();
}
}
// end::class[]

View File

@@ -44,8 +44,8 @@ public class Config {
CacheFactoryBean gemfireCache() { // <3>
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -6,9 +6,12 @@ apply plugin: 'spring-io'
description = "Aggregator for Spring Session and Spring Data Redis"
dependencies {
compile project(':spring-session'),
"org.springframework.data:spring-data-redis:$springDataRedisVersion",
"redis.clients:jedis:$jedisVersion",
compile project(':spring-session')
compile ("org.springframework.data:spring-data-redis:$springDataRedisVersion") {
exclude group: "org.slf4j", module: 'slf4j-api'
exclude group: "org.slf4j", module: 'jcl-over-slf4j'
}
compile "redis.clients:jedis:$jedisVersion",
"org.apache.commons:commons-pool2:$commonsPoolVersion"
}
@@ -18,4 +21,4 @@ dependencyManagement {
mavenBom "io.spring.platform:platform-bom:${springIoVersion}"
}
}
}
}

View File

@@ -15,6 +15,7 @@ configurations {
}
dependencies {
compile "commons-logging:commons-logging:$commonsLoggingVersion"
optional "org.springframework.data:spring-data-redis:$springDataRedisVersion",
"com.hazelcast:hazelcast:$hazelcastVersion",
"org.springframework.data:spring-data-gemfire:$springDataGemFireVersion",
@@ -25,7 +26,7 @@ dependencies {
"org.springframework:spring-messaging:$springVersion",
"org.springframework:spring-websocket:$springVersion"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"
integrationTestCompile "redis.clients:jedis:2.4.1",
integrationTestCompile "redis.clients:jedis:$jedisVersion",
"org.apache.commons:commons-pool2:2.2",
"com.hazelcast:hazelcast-client:$hazelcastVersion",
"com.h2database:h2:$h2Version",

View File

@@ -65,24 +65,25 @@ import static org.assertj.core.api.Assertions.assertThat;
public abstract class AbstractGemFireIntegrationTests {
protected static final boolean DEFAULT_ENABLE_QUERY_DEBUGGING = false;
protected static final boolean GEMFIRE_QUERY_DEBUG = Boolean
.getBoolean("spring.session.data.gemfire.query.debug");
protected static final boolean GEMFIRE_QUERY_DEBUG =
Boolean.getBoolean("spring.session.data.gemfire.query.debug");
protected static final int DEFAULT_GEMFIRE_SERVER_PORT = CacheServer.DEFAULT_PORT;
protected static final long DEFAULT_WAIT_DURATION = TimeUnit.SECONDS.toMillis(20);
protected static final long DEFAULT_WAIT_INTERVAL = 500L;
protected static final File WORKING_DIRECTORY = new File(
System.getProperty("user.dir"));
protected static final File WORKING_DIRECTORY =
new File(System.getProperty("user.dir"));
protected static final String DEFAULT_PROCESS_CONTROL_FILENAME = "process.ctl";
protected static final String GEMFIRE_LOG_FILE_NAME = System
.getProperty("spring.session.data.gemfire.log-file", "server.log");
protected static final String GEMFIRE_LOG_FILE_NAME =
System.getProperty("spring.session.data.gemfire.log-file", "server.log");
protected static final String GEMFIRE_LOG_LEVEL = System
.getProperty("spring.session.data.gemfire.log-level", "warning");
protected static final String GEMFIRE_LOG_LEVEL =
System.getProperty("spring.session.data.gemfire.log-level", "warning");
@Autowired
protected Cache gemfireCache;
@@ -93,7 +94,7 @@ public abstract class AbstractGemFireIntegrationTests {
@Before
public void setup() {
System.setProperty("gemfire.Query.VERBOSE",
String.valueOf(isQueryDebuggingEnabled()));
String.valueOf(isQueryDebuggingEnabled()));
}
/* (non-Javadoc) */

View File

@@ -32,7 +32,6 @@ import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.client.ClientCache;
import com.gemstone.gemfire.cache.client.Pool;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -49,7 +48,6 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.client.PoolFactoryBean;
import org.springframework.data.gemfire.config.GemfireConstants;
import org.springframework.data.gemfire.server.CacheServerFactoryBean;
import org.springframework.data.gemfire.support.ConnectionEndpoint;
import org.springframework.session.ExpiringSession;
@@ -78,10 +76,8 @@ import static org.assertj.core.api.Assertions.assertThat;
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
@@ -159,17 +155,17 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
public void setup() {
assertThat(GemFireUtils.isClient(gemfireCache)).isTrue();
Region<Object, ExpiringSession> springSessionGemFireRegion = gemfireCache
.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
Region<Object, ExpiringSession> springSessionGemFireRegion =
gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(springSessionGemFireRegion).isNotNull();
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes = springSessionGemFireRegion
.getAttributes();
RegionAttributes<Object, ExpiringSession> springSessionGemFireRegionAttributes =
springSessionGemFireRegion.getAttributes();
assertThat(springSessionGemFireRegionAttributes).isNotNull();
assertThat(springSessionGemFireRegionAttributes.getDataPolicy())
.isEqualTo(DataPolicy.EMPTY);
.isEqualTo(DataPolicy.EMPTY);
}
@After
@@ -270,54 +266,40 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name",
ClientServerGemFireOperationsSessionRepositoryIntegrationTests.class
.getName());
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setProperties(gemfireProperties());
return clientCacheFactory;
}
@Bean
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean() {
@Override
protected Properties resolveGemfireProperties() {
return gemfireProperties();
}
};
PoolFactoryBean poolFactory = new PoolFactoryBean();
poolFactory.setName(GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME);
poolFactory.setFreeConnectionTimeout(5000); // 5 seconds
poolFactory.setKeepAlive(false);
poolFactory.setMaxConnections(
SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
poolFactory.setMaxConnections(SpringSessionGemFireServerConfiguration.MAX_CONNECTIONS);
poolFactory.setPingInterval(TimeUnit.SECONDS.toMillis(5));
poolFactory.setReadTimeout(2000); // 2 seconds
poolFactory.setRetryAttempts(2);
poolFactory.setSubscriptionEnabled(true);
poolFactory.setThreadLocalConnections(false);
poolFactory
.setServerEndpoints(Collections.singletonList(new ConnectionEndpoint(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME,
port)));
poolFactory.setServers(Collections.singletonList(new ConnectionEndpoint(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)));
return poolFactory;
}
@Bean
ClientCacheFactoryBean gemfireCache(Pool gemfirePool) {
ClientCacheFactoryBean clientCacheFactory = new ClientCacheFactoryBean();
clientCacheFactory.setClose(true);
clientCacheFactory.setPool(gemfirePool);
clientCacheFactory.setProperties(gemfireProperties());
clientCacheFactory.setUseBeanFactoryLocator(false);
return clientCacheFactory;
}
@Bean
public SessionEventListener sessionEventListener() {
return new SessionEventListener();
@@ -335,7 +317,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
for (InetSocketAddress server : clientCache.getCurrentServers()) {
System.err.printf("GemFire Server [host: %1$s, port: %2$d]%n",
server.getHostName(), server.getPort());
server.getHostName(), server.getPort());
}
}
}
@@ -366,12 +348,12 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean cacheFactory = new CacheFactoryBean();
CacheFactoryBean gemfireCache = new CacheFactoryBean();
cacheFactory.setProperties(gemfireProperties());
cacheFactory.setUseBeanFactoryLocator(false);
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return cacheFactory;
return gemfireCache;
}
@Bean

View File

@@ -391,9 +391,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setLazyInitialize(false);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -253,9 +253,8 @@ public class EnableGemFireHttpSessionEventsIntegrationTests
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setLazyInitialize(false);
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
gemfireCache.setUseBeanFactoryLocator(false);
return gemfireCache;
}

View File

@@ -135,7 +135,6 @@ public class GemFireHttpSessionJavaConfigurationTests
cacheFactory.setClose(true);
cacheFactory.setProperties(gemfireProperties());
cacheFactory.setUseBeanFactoryLocator(false);
return cacheFactory;
}

View File

@@ -65,8 +65,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
* The default maximum interval in seconds in which a Session can remain inactive
* before it is considered expired.
*/
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS = (int) TimeUnit.MINUTES
.toSeconds(30);
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS =
(int) TimeUnit.MINUTES.toSeconds(30);
protected static final Class<Object> SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT = Object.class;
protected static final Class<GemFireSession> SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT = GemFireSession.class;
@@ -152,7 +152,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (this.clientRegionShortcut != null ? this.clientRegionShortcut
: DEFAULT_CLIENT_REGION_SHORTCUT);
: DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
@@ -175,7 +175,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
protected String[] getIndexableSessionAttributes() {
return (this.indexableSessionAttributes != null ? this.indexableSessionAttributes
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
}
/**
@@ -245,7 +245,7 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
protected RegionShortcut getServerRegionShortcut() {
return (this.serverRegionShortcut != null ? this.serverRegionShortcut
: DEFAULT_SERVER_REGION_SHORTCUT);
: DEFAULT_SERVER_REGION_SHORTCUT);
}
/**
@@ -269,8 +269,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
protected String getSpringSessionGemFireRegionName() {
return (StringUtils.hasText(this.springSessionGemFireRegionName)
? this.springSessionGemFireRegionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
? this.springSessionGemFireRegionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
/**
@@ -281,26 +281,24 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
* this @Configuration class.
*/
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes enableGemFireHttpSessionAnnotationAttributes = AnnotationAttributes
.fromMap(importMetadata.getAnnotationAttributes(
EnableGemFireHttpSession.class.getName()));
AnnotationAttributes enableGemFireHttpSessionAnnotationAttributes =
AnnotationAttributes.fromMap(importMetadata.getAnnotationAttributes(
EnableGemFireHttpSession.class.getName()));
setClientRegionShortcut(ClientRegionShortcut.class
.cast(enableGemFireHttpSessionAnnotationAttributes
.getEnum("clientRegionShortcut")));
setClientRegionShortcut(ClientRegionShortcut.class.cast(
enableGemFireHttpSessionAnnotationAttributes.getEnum("clientRegionShortcut")));
setIndexableSessionAttributes(enableGemFireHttpSessionAnnotationAttributes
.getStringArray("indexableSessionAttributes"));
.getStringArray("indexableSessionAttributes"));
setMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAnnotationAttributes
.getNumber("maxInactiveIntervalInSeconds").intValue());
.getNumber("maxInactiveIntervalInSeconds").intValue());
setServerRegionShortcut(
RegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes
.getEnum("serverRegionShortcut")));
setServerRegionShortcut(RegionShortcut.class.cast(
enableGemFireHttpSessionAnnotationAttributes.getEnum("serverRegionShortcut")));
setSpringSessionGemFireRegionName(
enableGemFireHttpSessionAnnotationAttributes.getString("regionName"));
enableGemFireHttpSessionAnnotationAttributes.getString("regionName"));
}
/**
@@ -316,11 +314,11 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
public GemFireOperationsSessionRepository sessionRepository(
@Qualifier("sessionRegionTemplate") GemfireOperations gemfireOperations) {
GemFireOperationsSessionRepository sessionRepository = new GemFireOperationsSessionRepository(
gemfireOperations);
GemFireOperationsSessionRepository sessionRepository =
new GemFireOperationsSessionRepository(gemfireOperations);
sessionRepository
.setMaxInactiveIntervalInSeconds(getMaxInactiveIntervalInSeconds());
sessionRepository.setMaxInactiveIntervalInSeconds(
getMaxInactiveIntervalInSeconds());
return sessionRepository;
}
@@ -339,8 +337,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
@Bean
@DependsOn(DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public GemfireTemplate sessionRegionTemplate(GemFireCache gemFireCache) {
return new GemfireTemplate(
gemFireCache.getRegion(getSpringSessionGemFireRegionName()));
return new GemfireTemplate(gemFireCache.getRegion(
getSpringSessionGemFireRegionName()));
}
/**
@@ -363,7 +361,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
GemFireCache gemfireCache,
RegionAttributes<Object, ExpiringSession> sessionRegionAttributes) {
GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> serverRegion = new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>();
GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> serverRegion =
new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>();
serverRegion.setGemfireCache(gemfireCache);
serverRegion.setClientRegionShortcut(getClientRegionShortcut());
@@ -390,19 +389,18 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
@Bean
@SuppressWarnings({ "unchecked", "deprecation" })
public RegionAttributesFactoryBean sessionRegionAttributes(
GemFireCache gemfireCache) {
public RegionAttributesFactoryBean sessionRegionAttributes(GemFireCache gemfireCache) {
RegionAttributesFactoryBean regionAttributes = new RegionAttributesFactoryBean();
regionAttributes.setKeyConstraint(SPRING_SESSION_GEMFIRE_REGION_KEY_CONSTRAINT);
regionAttributes
.setValueConstraint(SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT);
regionAttributes.setValueConstraint(SPRING_SESSION_GEMFIRE_REGION_VALUE_CONSTRAINT);
if (isExpirationAllowed(gemfireCache)) {
regionAttributes.setStatisticsEnabled(true);
regionAttributes.setEntryIdleTimeout(new ExpirationAttributes(
Math.max(getMaxInactiveIntervalInSeconds(), 0),
ExpirationAction.INVALIDATE));
Math.max(getMaxInactiveIntervalInSeconds(), 0),
ExpirationAction.INVALIDATE));
}
return regionAttributes;
@@ -421,8 +419,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
*/
boolean isExpirationAllowed(GemFireCache gemfireCache) {
return !(GemFireUtils.isClient(gemfireCache)
? GemFireUtils.isProxy(getClientRegionShortcut())
: GemFireUtils.isProxy(getServerRegionShortcut()));
? GemFireUtils.isProxy(getClientRegionShortcut())
: GemFireUtils.isProxy(getServerRegionShortcut()));
}
/**
@@ -486,9 +484,9 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
index.setCache(gemfireCache);
index.setName("sessionAttributesIndex");
index.setExpression(String.format("s.attributes[%1$s]",
getIndexableSessionAttributesAsGemFireIndexExpression()));
getIndexableSessionAttributesAsGemFireIndexExpression()));
index.setFrom(String.format("%1$s s",
GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName())));
GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName())));
index.setOverride(true);
return index;

View File

@@ -23,6 +23,8 @@ import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.RegionShortcut;
import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.gemfire.GenericRegionFactoryBean;
@@ -43,9 +45,12 @@ import org.springframework.util.StringUtils;
* @author John Blum
* @since 1.1.0
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.InitializingBean
*/
public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
implements FactoryBean<Region<K, V>>, InitializingBean {
implements BeanFactoryAware, FactoryBean<Region<K, V>>, InitializingBean {
protected static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT;
@@ -53,6 +58,8 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
protected static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME = GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
private BeanFactory beanFactory;
private ClientRegionShortcut clientRegionShortcut;
private GemFireCache gemfireCache;
@@ -138,6 +145,7 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
ClientRegionShortcut shortcut = getClientRegionShortcut();
clientRegion.setBeanFactory(getBeanFactory());
clientRegion.setCache(gemfireCache);
clientRegion.setAttributes(getRegionAttributes());
clientRegion.setInterests(registerInterests(!GemFireUtils.isLocal(shortcut)));
@@ -206,6 +214,36 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
return true;
}
/**
* Sets a reference to the Spring {@link BeanFactory} responsible for
* creating GemFire components.
*
* @param beanFactory reference to the Spring {@link BeanFactory}
* @see org.springframework.beans.factory.BeanFactory
* @throws IllegalArgumentException if the {@link BeanFactory} reference
* is null.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
/**
* Gets a reference to the Spring {@link BeanFactory} responsible for
* creating GemFire components.
*
* @return a reference to the Spring {@link BeanFactory}
* @throws IllegalStateException if the {@link BeanFactory} reference
* is null.
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null,
"A reference to the BeanFactory was not properly configured");
return this.beanFactory;
}
/**
* Sets the {@link Region} data policy used by the GemFire cache client to manage
* Session state.
@@ -240,7 +278,7 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
* @throws IllegalArgumentException if the {@link GemFireCache} reference is null.
*/
public void setGemfireCache(GemFireCache gemfireCache) {
Assert.notNull(gemfireCache, "The GemFireCache reference must not be null");
Assert.notNull(gemfireCache, "GemFireCache must not be null");
this.gemfireCache = gemfireCache;
}
@@ -253,7 +291,7 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
*/
protected GemFireCache getGemfireCache() {
Assert.state(this.gemfireCache != null,
"A reference to a GemFireCache was not properly configured");
"A reference to the GemFireCache was not properly configured");
return this.gemfireCache;
}

View File

@@ -25,16 +25,18 @@ import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.util.StringUtils;
/**
* Configuration class registering {@code MongoSessionRepository} bean. To import this
* configuration use {@link EnableMongoHttpSession} annotation.
*
* @author Jakub Kubrynski
* @author Eddú Meléndez
* @since 1.2
*/
@Configuration
class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration
public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
private AbstractMongoSessionConverter mongoSessionConverter;
@@ -43,18 +45,28 @@ class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration
private String collectionName;
@Bean
MongoOperationsSessionRepository mongoSessionRepository(
public MongoOperationsSessionRepository mongoSessionRepository(
MongoOperations mongoOperations) {
MongoOperationsSessionRepository repository = new MongoOperationsSessionRepository(
mongoOperations);
repository.setCollectionName(this.collectionName);
repository.setMaxInactiveIntervalInSeconds(this.maxInactiveIntervalInSeconds);
if (this.mongoSessionConverter != null) {
repository.setMongoSessionConverter(this.mongoSessionConverter);
}
if (StringUtils.hasText(this.collectionName)) {
repository.setCollectionName(this.collectionName);
}
return repository;
}
public void setCollectionName(String collectionName) {
this.collectionName = collectionName;
}
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importMetadata
.getAnnotationAttributes(EnableMongoHttpSession.class.getName()));

View File

@@ -771,6 +771,9 @@ public class RedisOperationsSessionRepository implements
* session.
*/
private void saveDelta() {
if (this.delta.isEmpty()) {
return;
}
String sessionId = getId();
getSessionBoundHashOperations(sessionId).putAll(this.delta);
String principalSessionKey = getSessionAttrNameKey(
@@ -781,7 +784,7 @@ public class RedisOperationsSessionRepository implements
|| this.delta.containsKey(securityPrincipalSessionKey)) {
if (this.originalPrincipalName != null) {
String originalPrincipalRedisKey = getPrincipalKey(
(String) this.originalPrincipalName);
this.originalPrincipalName);
RedisOperationsSessionRepository.this.sessionRedisOperations
.boundSetOps(originalPrincipalRedisKey).remove(sessionId);
}

View File

@@ -16,9 +16,11 @@
package org.springframework.session.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -36,11 +38,12 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
@@ -84,20 +87,35 @@ import org.springframework.util.StringUtils;
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/">
* Spring Framework Reference Documentation</a>.
* <p>
* By default, this implementation uses <code>SPRING_SESSION</code> table to store
* sessions. Note that the table name can be customized using the
* {@link #setTableName(String)} method.
* By default, this implementation uses <code>SPRING_SESSION</code> and
* <code>SPRING_SESSION_ATTRIBUTES</code> tables to store sessions. Note that the table
* name can be customized using the {@link #setTableName(String)} method. In that case the
* table used to store attributes will be named using the provided table name, suffixed
* with <code>_ATTRIBUTES</code>.
*
* Depending on your database, the table definition can be described as below:
*
* <pre class="code">
* CREATE TABLE SPRING_SESSION (
* SESSION_ID CHAR(36),
* CREATION_TIME BIGINT NOT NULL,
* LAST_ACCESS_TIME BIGINT NOT NULL,
* MAX_INACTIVE_INTERVAL INT NOT NULL,
* PRINCIPAL_NAME VARCHAR(100),
* SESSION_BYTES BLOB,
* CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
* );
*
* CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
*
* CREATE TABLE SPRING_SESSION_ATTRIBUTES (
* SESSION_ID CHAR(36),
* ATTRIBUTE_NAME VARCHAR(100),
* ATTRIBUTE_BYTES BYTEA,
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
* CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
* );
*
* CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);
* </pre>
*
* Due to the differences between the various database vendors, especially when it comes
@@ -114,19 +132,49 @@ public class JdbcOperationsSessionRepository implements
private static final String DEFAULT_TABLE_NAME = "SPRING_SESSION";
private static final String CREATE_SESSION_QUERY = "INSERT INTO %TABLE_NAME%(SESSION_ID, LAST_ACCESS_TIME, PRINCIPAL_NAME, SESSION_BYTES) VALUES (?, ?, ?, ?)";
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String GET_SESSION_QUERY = "SELECT LAST_ACCESS_TIME, SESSION_BYTES FROM %TABLE_NAME% WHERE SESSION_ID = ?";
private static final String CREATE_SESSION_QUERY =
"INSERT INTO %TABLE_NAME%(SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, PRINCIPAL_NAME) " +
"VALUES (?, ?, ?, ?, ?)";
private static final String UPDATE_SESSION_QUERY = "UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ?, PRINCIPAL_NAME = ?, SESSION_BYTES = ? WHERE SESSION_ID = ?";
private static final String CREATE_SESSION_ATTRIBUTE_QUERY =
"INSERT INTO %TABLE_NAME%_ATTRIBUTES(SESSION_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES) " +
"VALUES (?, ?, ?)";
private static final String UPDATE_SESSION_LAST_ACCESS_TIME_QUERY = "UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ? WHERE SESSION_ID = ?";
private static final String GET_SESSION_QUERY =
"SELECT S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " +
"FROM %TABLE_NAME% S " +
"LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.SESSION_ID = SA.SESSION_ID " +
"WHERE S.SESSION_ID = ?";
private static final String DELETE_SESSION_QUERY = "DELETE FROM %TABLE_NAME% WHERE SESSION_ID = ?";
private static final String UPDATE_SESSION_QUERY =
"UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ?, MAX_INACTIVE_INTERVAL = ?, PRINCIPAL_NAME = ? " +
"WHERE SESSION_ID = ?";
private static final String LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY = "SELECT LAST_ACCESS_TIME, SESSION_BYTES FROM %TABLE_NAME% WHERE PRINCIPAL_NAME = ?";
private static final String UPDATE_SESSION_ATTRIBUTE_QUERY =
"UPDATE %TABLE_NAME%_ATTRIBUTES SET ATTRIBUTE_BYTES = ? " +
"WHERE SESSION_ID = ? " +
"AND ATTRIBUTE_NAME = ?";
private static final String DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY = "DELETE FROM %TABLE_NAME% WHERE LAST_ACCESS_TIME < ?";
private static final String DELETE_SESSION_ATTRIBUTE_QUERY =
"DELETE FROM %TABLE_NAME%_ATTRIBUTES " +
"WHERE SESSION_ID = ? " +
"AND ATTRIBUTE_NAME = ?";
private static final String DELETE_SESSION_QUERY =
"DELETE FROM %TABLE_NAME% " +
"WHERE SESSION_ID = ?";
private static final String LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY =
"SELECT S.SESSION_ID, S.CREATION_TIME, S.LAST_ACCESS_TIME, S.MAX_INACTIVE_INTERVAL, SA.ATTRIBUTE_NAME, SA.ATTRIBUTE_BYTES " +
"FROM %TABLE_NAME% S " +
"LEFT OUTER JOIN %TABLE_NAME%_ATTRIBUTES SA ON S.SESSION_ID = SA.SESSION_ID " +
"WHERE S.PRINCIPAL_NAME = ?";
private static final String DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY =
"DELETE FROM %TABLE_NAME% " +
"WHERE LAST_ACCESS_TIME < ?";
private static final Log logger = LogFactory
.getLog(JdbcOperationsSessionRepository.class);
@@ -231,9 +279,27 @@ public class JdbcOperationsSessionRepository implements
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime());
ps.setString(3, session.getPrincipalName());
serialize(ps, 4, session.delegate);
ps.setLong(2, session.getCreationTime());
ps.setLong(3, session.getLastAccessedTime());
ps.setInt(4, session.getMaxInactiveIntervalInSeconds());
ps.setString(5, session.getPrincipalName());
}
});
final List<String> attributeNames = new ArrayList<String>(session.getAttributeNames());
JdbcOperationsSessionRepository.this.jdbcOperations.batchUpdate(
getQuery(CREATE_SESSION_ATTRIBUTE_QUERY),
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
String attributeName = attributeNames.get(i);
ps.setString(1, session.getId());
ps.setString(2, attributeName);
serialize(ps, 3, session.getAttribute(attributeName));
}
public int getBatchSize() {
return attributeNames.size();
}
});
@@ -242,10 +308,10 @@ public class JdbcOperationsSessionRepository implements
});
}
else {
if (session.isAttributesChanged()) {
this.transactionOperations.execute(new TransactionCallbackWithoutResult() {
this.transactionOperations.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
protected void doInTransactionWithoutResult(TransactionStatus status) {
if (session.isChanged()) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(UPDATE_SESSION_QUERY),
new PreparedStatementSetter() {
@@ -253,55 +319,84 @@ public class JdbcOperationsSessionRepository implements
public void setValues(PreparedStatement ps)
throws SQLException {
ps.setLong(1, session.getLastAccessedTime());
ps.setString(2, session.getPrincipalName());
serialize(ps, 3, session.delegate);
ps.setInt(2, session.getMaxInactiveIntervalInSeconds());
ps.setString(3, session.getPrincipalName());
ps.setString(4, session.getId());
}
});
}
Map<String, Object> delta = session.getDelta();
if (!delta.isEmpty()) {
for (final Map.Entry<String, Object> entry : delta.entrySet()) {
if (entry.getValue() == null) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(DELETE_SESSION_ATTRIBUTE_QUERY),
new PreparedStatementSetter() {
});
}
else if (session.isLastAccessTimeChanged()) {
this.transactionOperations.execute(new TransactionCallbackWithoutResult() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, session.getId());
ps.setString(2, entry.getKey());
}
protected void doInTransactionWithoutResult(TransactionStatus status) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(UPDATE_SESSION_LAST_ACCESS_TIME_QUERY),
new PreparedStatementSetter() {
});
}
else {
int updatedCount = JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(UPDATE_SESSION_ATTRIBUTE_QUERY),
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
ps.setLong(1, session.getLastAccessedTime());
ps.setString(2, session.getId());
}
public void setValues(PreparedStatement ps) throws SQLException {
serialize(ps, 1, entry.getValue());
ps.setString(2, session.getId());
ps.setString(3, entry.getKey());
}
});
});
if (updatedCount == 0) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
getQuery(CREATE_SESSION_ATTRIBUTE_QUERY),
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, session.getId());
ps.setString(2, entry.getKey());
serialize(ps, 3, entry.getValue());
}
});
}
}
}
}
}
});
}
else {
return;
}
});
}
session.clearChangeFlags();
}
public JdbcSession getSession(final String id) {
ExpiringSession session = this.transactionOperations.execute(new TransactionCallback<ExpiringSession>() {
final ExpiringSession session = this.transactionOperations.execute(new TransactionCallback<ExpiringSession>() {
public ExpiringSession doInTransaction(TransactionStatus status) {
try {
return JdbcOperationsSessionRepository.this.jdbcOperations.queryForObject(
getQuery(GET_SESSION_QUERY),
new Object[] { id },
JdbcOperationsSessionRepository.this.mapper);
}
catch (EmptyResultDataAccessException ignored) {
List<ExpiringSession> sessions = JdbcOperationsSessionRepository.this.jdbcOperations.query(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(getQuery(GET_SESSION_QUERY),
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setString(1, id);
return ps;
}
},
JdbcOperationsSessionRepository.this.mapper
);
if (sessions.isEmpty()) {
return null;
}
return sessions.get(0);
}
});
@@ -338,9 +433,19 @@ public class JdbcOperationsSessionRepository implements
public List<ExpiringSession> doInTransaction(TransactionStatus status) {
return JdbcOperationsSessionRepository.this.jdbcOperations.query(
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY),
new Object[] { indexValue },
JdbcOperationsSessionRepository.this.mapper);
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY),
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setString(1, indexValue);
return ps;
}
},
JdbcOperationsSessionRepository.this.mapper
);
}
});
@@ -402,9 +507,9 @@ public class JdbcOperationsSessionRepository implements
private static GenericConversionService createDefaultConversionService() {
GenericConversionService converter = new GenericConversionService();
converter.addConverter(ExpiringSession.class, byte[].class,
converter.addConverter(Object.class, byte[].class,
new SerializingConverter());
converter.addConverter(byte[].class, ExpiringSession.class,
converter.addConverter(byte[].class, Object.class,
new DeserializingConverter());
return converter;
}
@@ -413,20 +518,20 @@ public class JdbcOperationsSessionRepository implements
return StringUtils.replace(base, "%TABLE_NAME%", this.tableName);
}
private void serialize(PreparedStatement ps, int paramIndex, ExpiringSession session)
private void serialize(PreparedStatement ps, int paramIndex, Object attributeValue)
throws SQLException {
this.lobHandler.getLobCreator().setBlobAsBytes(ps, paramIndex,
(byte[]) this.conversionService.convert(session,
TypeDescriptor.valueOf(ExpiringSession.class),
(byte[]) this.conversionService.convert(attributeValue,
TypeDescriptor.valueOf(Object.class),
TypeDescriptor.valueOf(byte[].class)));
}
private ExpiringSession deserialize(ResultSet rs, String columnName)
private Object deserialize(ResultSet rs, String columnName)
throws SQLException {
return (ExpiringSession) this.conversionService.convert(
return this.conversionService.convert(
this.lobHandler.getBlobAsBytes(rs, columnName),
TypeDescriptor.valueOf(byte[].class),
TypeDescriptor.valueOf(ExpiringSession.class));
TypeDescriptor.valueOf(Object.class));
}
/**
@@ -440,9 +545,9 @@ public class JdbcOperationsSessionRepository implements
private boolean isNew;
private boolean lastAccessTimeChanged;
private boolean changed;
private boolean attributesChanged;
private Map<String, Object> delta = new HashMap<String, Object>();
JdbcSession() {
this.delegate = new MapSession();
@@ -454,54 +559,28 @@ public class JdbcOperationsSessionRepository implements
this.delegate = delegate;
}
public boolean isNew() {
boolean isNew() {
return this.isNew;
}
public boolean isLastAccessTimeChanged() {
return this.lastAccessTimeChanged;
boolean isChanged() {
return this.changed;
}
public boolean isAttributesChanged() {
return this.attributesChanged;
Map<String, Object> getDelta() {
return this.delta;
}
public void clearChangeFlags() {
void clearChangeFlags() {
this.isNew = false;
this.lastAccessTimeChanged = false;
this.attributesChanged = false;
this.changed = false;
this.delta.clear();
}
public String getPrincipalName() {
String getPrincipalName() {
return PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
}
public long getCreationTime() {
return this.delegate.getCreationTime();
}
public void setLastAccessedTime(long lastAccessedTime) {
this.delegate.setLastAccessedTime(lastAccessedTime);
this.lastAccessTimeChanged = true;
}
public long getLastAccessedTime() {
return this.delegate.getLastAccessedTime();
}
public void setMaxInactiveIntervalInSeconds(int interval) {
this.delegate.setMaxInactiveIntervalInSeconds(interval);
this.attributesChanged = true;
}
public int getMaxInactiveIntervalInSeconds() {
return this.delegate.getMaxInactiveIntervalInSeconds();
}
public boolean isExpired() {
return this.delegate.isExpired();
}
public String getId() {
return this.delegate.getId();
}
@@ -516,12 +595,42 @@ public class JdbcOperationsSessionRepository implements
public void setAttribute(String attributeName, Object attributeValue) {
this.delegate.setAttribute(attributeName, attributeValue);
this.attributesChanged = true;
this.delta.put(attributeName, attributeValue);
if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||
SPRING_SECURITY_CONTEXT.equals(attributeName)) {
this.changed = true;
}
}
public void removeAttribute(String attributeName) {
this.delegate.removeAttribute(attributeName);
this.attributesChanged = true;
this.delta.put(attributeName, null);
}
public long getCreationTime() {
return this.delegate.getCreationTime();
}
public void setLastAccessedTime(long lastAccessedTime) {
this.delegate.setLastAccessedTime(lastAccessedTime);
this.changed = true;
}
public long getLastAccessedTime() {
return this.delegate.getLastAccessedTime();
}
public void setMaxInactiveIntervalInSeconds(int interval) {
this.delegate.setMaxInactiveIntervalInSeconds(interval);
this.changed = true;
}
public int getMaxInactiveIntervalInSeconds() {
return this.delegate.getMaxInactiveIntervalInSeconds();
}
public boolean isExpired() {
return this.delegate.isExpired();
}
}
@@ -533,8 +642,6 @@ public class JdbcOperationsSessionRepository implements
*/
static class PrincipalNameResolver {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private SpelExpressionParser parser = new SpelExpressionParser();
public String resolvePrincipal(Session session) {
@@ -556,8 +663,19 @@ public class JdbcOperationsSessionRepository implements
private class ExpiringSessionMapper implements RowMapper<ExpiringSession> {
public ExpiringSession mapRow(ResultSet rs, int rowNum) throws SQLException {
ExpiringSession session = deserialize(rs, "SESSION_BYTES");
MapSession session = new MapSession(rs.getString("SESSION_ID"));
session.setCreationTime(rs.getLong("CREATION_TIME"));
session.setLastAccessedTime(rs.getLong("LAST_ACCESS_TIME"));
session.setMaxInactiveIntervalInSeconds(rs.getInt("MAX_INACTIVE_INTERVAL"));
String attributeName = rs.getString("ATTRIBUTE_NAME");
if (attributeName != null) {
session.setAttribute(attributeName, deserialize(rs, "ATTRIBUTE_BYTES"));
while (rs.next() && session.getId().equals(rs.getString("SESSION_ID"))) {
session.setAttribute(rs.getString("ATTRIBUTE_NAME"),
deserialize(rs, "ATTRIBUTE_BYTES"));
}
rs.previous();
}
return session;
}

View File

@@ -45,6 +45,7 @@ import org.springframework.util.StringUtils;
* {@link DataSource} must be exposed as a Bean.
*
* @author Vedran Pavic
* @author Eddú Meléndez
* @since 1.2.0
* @see EnableJdbcHttpSession
*/
@@ -106,6 +107,14 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
this.springSessionConversionService = conversionService;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setMaxInactiveIntervalInSeconds(Integer maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
private String getTableName() {
if (StringUtils.hasText(this.tableName)) {
return this.tableName;

View File

@@ -42,7 +42,7 @@ import org.springframework.web.util.UrlPathHelper;
* The configuration:
* </p>
* <ul>
* <li>Ensures the the {@link Session} is kept alive on incoming web socket messages.</li>
* <li>Ensures the {@link Session} is kept alive on incoming web socket messages.</li>
* <li>Ensures that Web Socket Sessions are destroyed when a {@link Session} is terminated
* </li>
* </ul>

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES BLOB,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES BLOB,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES BLOB,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES BLOB,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES LONGVARBINARY,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES LONGVARBINARY,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES LONGVARBINARY,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES LONGVARBINARY,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES BLOB,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES BLOB,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME NUMBER(19,0) NOT NULL,
LAST_ACCESS_TIME NUMBER(19,0) NOT NULL,
MAX_INACTIVE_INTERVAL NUMBER(10,0) NOT NULL,
PRINCIPAL_NAME VARCHAR2(100 CHAR),
SESSION_BYTES BLOB,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR2(100 CHAR),
ATTRIBUTE_BYTES BLOB,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES BYTEA,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES BYTEA,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHARACTER(36),
CREATION_TIME INTEGER NOT NULL,
LAST_ACCESS_TIME INTEGER NOT NULL,
MAX_INACTIVE_INTERVAL INTEGER NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES BLOB,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES BLOB,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES IMAGE,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
);
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES IMAGE,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
);
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -1,9 +1,20 @@
CREATE TABLE SPRING_SESSION (
SESSION_ID CHAR(36),
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
SESSION_BYTES IMAGE,
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (SESSION_ID)
) LOCK DATAROWS;
CREATE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (LAST_ACCESS_TIME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_ID CHAR(36),
ATTRIBUTE_NAME VARCHAR(100),
ATTRIBUTE_BYTES IMAGE,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_ID) REFERENCES SPRING_SESSION(SESSION_ID) ON DELETE CASCADE
) LOCK DATAROWS;
CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_ID);

View File

@@ -32,6 +32,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.gemfire.client.Interest;
import org.springframework.session.ExpiringSession;
@@ -47,6 +48,7 @@ import static org.mockito.Mockito.mock;
* @since 1.1.0
* @see org.junit.Rule
* @see org.junit.Test
* @see org.junit.rules.ExpectedException
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.
* GemFireCacheTypeAwareRegionFactoryBean
@@ -63,14 +65,16 @@ import static org.mockito.Mockito.mock;
public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException exception = ExpectedException.none();
@Mock
ClientCache mockClientCache;
@Mock
Region<Object, ExpiringSession> mockClientRegion;
@Mock
Region<Object, ExpiringSession> mockServerRegion;
@Mock
ClientCache mockClientCache;
private GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> regionFactoryBean;
@@ -164,13 +168,37 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
assertThat(this.regionFactoryBean.isSingleton()).isTrue();
}
@Test
public void setAndGetBeanFactory() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
this.regionFactoryBean.setBeanFactory(mockBeanFactory);
assertThat(this.regionFactoryBean.getBeanFactory()).isEqualTo(mockBeanFactory);
}
@Test
public void setBeanFactoryToNullThrowsIllegalArgumentException() {
this.exception.expect(IllegalArgumentException.class);
this.exception.expectMessage("BeanFactory must not be null");
this.regionFactoryBean.setBeanFactory(null);
}
@Test
public void getBeanFactoryWhenNullThrowsIllegalStateException() {
this.exception.expect(IllegalStateException.class);
this.exception.expectMessage(
"A reference to the BeanFactory was not properly configured");
this.regionFactoryBean.getBeanFactory();
}
@Test
public void setAndGetClientRegionShortcut() {
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
this.regionFactoryBean
.setClientRegionShortcut(ClientRegionShortcut.LOCAL_PERSISTENT);
this.regionFactoryBean.setClientRegionShortcut(
ClientRegionShortcut.LOCAL_PERSISTENT);
assertThat(this.regionFactoryBean.getClientRegionShortcut())
.isEqualTo(ClientRegionShortcut.LOCAL_PERSISTENT);
@@ -192,17 +220,16 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Test
public void setGemfireCacheToNullThrowsIllegalArgumentException() {
this.expectedException.expect(IllegalArgumentException.class);
this.expectedException
.expectMessage("The GemFireCache reference must not be null");
this.exception.expect(IllegalArgumentException.class);
this.exception.expectMessage("GemFireCache must not be null");
this.regionFactoryBean.setGemfireCache(null);
}
@Test
public void getGemfireCacheWhenNullThrowsIllegalStateException() {
this.expectedException.expect(IllegalStateException.class);
this.expectedException.expectMessage(
"A reference to a GemFireCache was not properly configured");
this.exception.expect(IllegalStateException.class);
this.exception.expectMessage(
"A reference to the GemFireCache was not properly configured");
this.regionFactoryBean.getGemfireCache();
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2014-2016 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.data.mongo.config.annotation.web.http;
import java.net.UnknownHostException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
/**
* @author Eddú Meléndez
*/
public class MongoHttpSessionConfigurationTest {
private AnnotationConfigApplicationContext context;
@Before
public void before() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void after() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void defaultCollectionName() {
registerAndRefresh(DefaultConfiguration.class);
MongoHttpSessionConfiguration session = this.context
.getBean(MongoHttpSessionConfiguration.class);
assertThat(session).isNotNull();
assertThat(ReflectionTestUtils.getField(session, "collectionName")).isEqualTo(
"sessions");
MongoOperationsSessionRepository repository = this.context.getBean(MongoOperationsSessionRepository.class);
assertThat(ReflectionTestUtils.getField(repository, "collectionName")).isEqualTo(
"sessions");
}
@Test
public void customCollectionName() {
registerAndRefresh(CustomCollectionNameConfiguration.class);
MongoHttpSessionConfiguration session = this.context
.getBean(MongoHttpSessionConfiguration.class);
assertThat(session).isNotNull();
assertThat(ReflectionTestUtils.getField(session, "collectionName")).isEqualTo(
"testSessions");
MongoOperationsSessionRepository repository = this.context.getBean(MongoOperationsSessionRepository.class);
assertThat(ReflectionTestUtils.getField(repository, "collectionName")).isEqualTo(
"testSessions");
}
@Test
public void setCustomCollectionName() {
registerAndRefresh(CustomConfiguration.class, CustomCollectionNameSetConfiguration.class);
MongoHttpSessionConfiguration session = this.context
.getBean(MongoHttpSessionConfiguration.class);
assertThat(session).isNotNull();
assertThat(ReflectionTestUtils.getField(session, "collectionName")).isEqualTo(
"customSession");
MongoOperationsSessionRepository repository = this.context.getBean(MongoOperationsSessionRepository.class);
assertThat(ReflectionTestUtils.getField(repository, "collectionName")).isEqualTo(
"customSession");
}
@Test
public void customMaxInactiveIntervalInSeconds() {
registerAndRefresh(CustomConfiguration.class, CustomMaxInactiveIntervalInSecondsSetConfiguration.class);
MongoHttpSessionConfiguration session = this.context
.getBean(MongoHttpSessionConfiguration.class);
assertThat(session).isNotNull();
assertThat(ReflectionTestUtils.getField(session, "maxInactiveIntervalInSeconds")).isEqualTo(
10);
MongoOperationsSessionRepository repository = this.context.getBean(MongoOperationsSessionRepository.class);
assertThat(ReflectionTestUtils.getField(repository, "maxInactiveIntervalInSeconds")).isEqualTo(
10);
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
}
static class BaseConfiguration {
@Bean
public MongoOperations mongoOperations() throws UnknownHostException {
MongoOperations mongoOperations = mock(MongoOperations.class);
IndexOperations indexOperations = mock(IndexOperations.class);
given(mongoOperations.indexOps(anyString())).willReturn(indexOperations);
return mongoOperations;
}
}
@Configuration
@EnableMongoHttpSession(collectionName = "testSessions")
static class CustomCollectionNameConfiguration extends BaseConfiguration {
}
@Configuration
@EnableMongoHttpSession
static class DefaultConfiguration extends BaseConfiguration {
}
@Configuration
static class CustomCollectionNameSetConfiguration extends MongoHttpSessionConfiguration {
CustomCollectionNameSetConfiguration() {
setCollectionName("customSession");
}
}
@Configuration
static class CustomMaxInactiveIntervalInSecondsSetConfiguration extends MongoHttpSessionConfiguration {
CustomMaxInactiveIntervalInSecondsSetConfiguration() {
setMaxInactiveIntervalInSeconds(10);
}
}
@Configuration
static class CustomConfiguration extends BaseConfiguration {
}
}

View File

@@ -166,6 +166,16 @@ public class RedisOperationsSessionRepositoryTests {
.isEqualTo(session.getCreationTime());
}
// gh-467
@Test
public void saveSessionNothingChanged() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
this.redisRepository.save(session);
verifyZeroInteractions(this.redisOperations);
}
@Test
public void saveJavadocSummary() {
RedisSession session = this.redisRepository.createSession();
@@ -202,6 +212,7 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void saveJavadoc() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
session.setLastAccessedTime(session.getLastAccessedTime());
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id"))
.willReturn(this.boundHashOperations);

View File

@@ -16,6 +16,7 @@
package org.springframework.session.jdbc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -31,6 +32,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -44,7 +46,6 @@ import org.springframework.transaction.TransactionDefinition;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.AdditionalMatchers.and;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.contains;
@@ -200,7 +201,7 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(session.isNew()).isFalse();
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).update(
and(startsWith("UPDATE"), contains("SESSION_BYTES")),
and(startsWith("UPDATE"), contains("ATTRIBUTE_BYTES")),
isA(PreparedStatementSetter.class));
}
@@ -215,7 +216,7 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(session.isNew()).isFalse();
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).update(
and(startsWith("UPDATE"), not(contains("SESSION_BYTES"))),
and(startsWith("UPDATE"), contains("LAST_ACCESS_TIME")),
isA(PreparedStatementSetter.class));
}
@@ -239,8 +240,8 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(session).isNull();
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { sessionId }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(
isA(PreparedStatementCreator.class), isA(RowMapper.class));
}
@Test
@@ -248,17 +249,16 @@ public class JdbcOperationsSessionRepositoryTests {
MapSession expired = new MapSession();
expired.setLastAccessedTime(System.currentTimeMillis() -
(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
given(this.jdbcOperations.queryForObject(startsWith("SELECT"),
eq(new Object[] { expired.getId() }), isA(RowMapper.class)))
.willReturn(expired);
given(this.jdbcOperations.query(isA(PreparedStatementCreator.class),
isA(RowMapper.class))).willReturn(Collections.singletonList(expired));
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.getSession(expired.getId());
assertThat(session).isNull();
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { expired.getId() }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(
isA(PreparedStatementCreator.class), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"),
eq(expired.getId()));
}
@@ -267,9 +267,8 @@ public class JdbcOperationsSessionRepositoryTests {
public void getSessionFound() {
MapSession saved = new MapSession();
saved.setAttribute("savedName", "savedValue");
given(this.jdbcOperations.queryForObject(startsWith("SELECT"),
eq(new Object[] { saved.getId() }), isA(RowMapper.class)))
.willReturn(saved);
given(this.jdbcOperations.query(isA(PreparedStatementCreator.class),
isA(RowMapper.class))).willReturn(Collections.singletonList(saved));
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.getSession(saved.getId());
@@ -278,8 +277,8 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(session.isNew()).isFalse();
assertThat(session.getAttribute("savedName")).isEqualTo("savedValue");
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { saved.getId() }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(
isA(PreparedStatementCreator.class), isA(RowMapper.class));
}
@Test
@@ -314,8 +313,8 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(sessions).isEmpty();
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(
isA(PreparedStatementCreator.class), isA(RowMapper.class));
}
@Test
@@ -330,8 +329,8 @@ public class JdbcOperationsSessionRepositoryTests {
MapSession saved2 = new MapSession();
saved2.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
saved.add(saved2);
given(this.jdbcOperations.query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class))).willReturn(saved);
given(this.jdbcOperations.query(isA(PreparedStatementCreator.class),
isA(RowMapper.class))).willReturn(saved);
Map<String, JdbcOperationsSessionRepository.JdbcSession> sessions = this.repository
.findByIndexNameAndIndexValue(
@@ -340,8 +339,8 @@ public class JdbcOperationsSessionRepositoryTests {
assertThat(sessions).hasSize(2);
assertPropagationRequiresNew();
verify(this.jdbcOperations, times(1)).query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(
isA(PreparedStatementCreator.class), isA(RowMapper.class));
}
@Test

View File

@@ -40,6 +40,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link JdbcHttpSessionConfiguration}.
*
* @author Vedran Pavic
* @author Eddú Meléndez
* @since 1.2.0
*/
public class JdbcHttpSessionConfigurationTests {
@@ -107,6 +108,30 @@ public class JdbcHttpSessionConfigurationTests {
}
}
@Test
public void setCustomTableName() {
registerAndRefresh(BaseConfiguration.class,
CustomTableNameSetConfiguration.class);
JdbcHttpSessionConfiguration repository = this.context
.getBean(JdbcHttpSessionConfiguration.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "tableName")).isEqualTo(
"custom_session");
}
@Test
public void setCustomMaxInactiveIntervalInSeconds() {
registerAndRefresh(BaseConfiguration.class,
CustomMaxInactiveIntervalInSecondsSetConfiguration.class);
JdbcHttpSessionConfiguration repository = this.context
.getBean(JdbcHttpSessionConfiguration.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "maxInactiveIntervalInSeconds")).isEqualTo(
10);
}
@Test
public void customMaxInactiveIntervalInSeconds() {
registerAndRefresh(CustomMaxInactiveIntervalInSecondsConfiguration.class);
@@ -180,6 +205,24 @@ public class JdbcHttpSessionConfigurationTests {
static class CustomTableNameConfiguration extends BaseConfiguration {
}
@Configuration
static class CustomTableNameSetConfiguration extends JdbcHttpSessionConfiguration {
CustomTableNameSetConfiguration() {
setTableName("custom_session");
}
}
@Configuration
static class CustomMaxInactiveIntervalInSecondsSetConfiguration extends JdbcHttpSessionConfiguration {
CustomMaxInactiveIntervalInSecondsSetConfiguration() {
setMaxInactiveIntervalInSeconds(10);
}
}
@Configuration
@EnableJdbcHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class CustomMaxInactiveIntervalInSecondsConfiguration