Additional Checkstyle Fixes

Issue gh-393
This commit is contained in:
Rob Winch
2016-03-07 15:32:03 -06:00
parent 7f3302253b
commit f0200696ef
189 changed files with 4591 additions and 3201 deletions

View File

@@ -1,7 +1,25 @@
/*
* 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;
import java.util.UUID;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
@@ -10,8 +28,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.UUID;
/**
* Base class for repositories integration tests
*
@@ -30,14 +46,18 @@ public abstract class AbstractITests {
@Before
public void setup() {
if (registry != null) {
registry.clear();
if (this.registry != null) {
this.registry.clear();
}
context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
changedContext = SecurityContextHolder.createEmptyContext();
changedContext.setAuthentication(new UsernamePasswordAuthenticationToken("changedContext-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext = SecurityContextHolder.createEmptyContext();
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
"changedContext-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
}
}

View File

@@ -45,12 +45,14 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
}
@SuppressWarnings("unchecked")
public <E extends AbstractSessionEvent> E getEvent(String sessionId) throws InterruptedException {
public <E extends AbstractSessionEvent> E getEvent(String sessionId)
throws InterruptedException {
return (E) waitForEvent(sessionId);
}
@SuppressWarnings("unchecked")
private <E extends AbstractSessionEvent> E waitForEvent(String sessionId) throws InterruptedException {
private <E extends AbstractSessionEvent> E waitForEvent(String sessionId)
throws InterruptedException {
Object lock = getLock(sessionId);
synchronized (lock) {
if (!this.events.containsKey(sessionId)) {

View File

@@ -47,8 +47,8 @@ import org.springframework.session.events.AbstractSessionEvent;
import static org.assertj.core.api.Assertions.assertThat;
/**
* AbstractGemFireIntegrationTests is an abstract base class encapsulating common operations for writing
* Spring Session GemFire integration tests.
* AbstractGemFireIntegrationTests is an abstract base class encapsulating common
* operations for writing Spring Session GemFire integration tests.
*
* @author John Blum
* @since 1.1.0
@@ -65,22 +65,24 @@ 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;
@@ -90,15 +92,17 @@ public abstract class AbstractGemFireIntegrationTests {
@Before
public void setup() {
System.setProperty("gemfire.Query.VERBOSE", String.valueOf(isQueryDebuggingEnabled()));
System.setProperty("gemfire.Query.VERBOSE",
String.valueOf(isQueryDebuggingEnabled()));
}
/* (non-Javadoc) */
protected static File createDirectory(String pathname) {
File directory = new File(WORKING_DIRECTORY, pathname);
assertThat(directory.isDirectory() || directory.mkdirs()).as(
String.format("Failed to create directory (%1$s)", directory)).isTrue();
assertThat(directory.isDirectory() || directory.mkdirs())
.as(String.format("Failed to create directory (%1$s)", directory))
.isTrue();
directory.deleteOnExit();
@@ -106,7 +110,8 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static List<String> createJavaProcessCommandLine(Class<?> type, String... args) {
protected static List<String> createJavaProcessCommandLine(Class<?> type,
String... args) {
List<String> commandLine = new ArrayList<String>();
String javaHome = System.getProperty("java.home");
@@ -117,7 +122,8 @@ public abstract class AbstractGemFireIntegrationTests {
commandLine.add("-ea");
commandLine.add(String.format("-Dgemfire.log-file=%1$s", GEMFIRE_LOG_FILE_NAME));
commandLine.add(String.format("-Dgemfire.log-level=%1$s", GEMFIRE_LOG_LEVEL));
commandLine.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
commandLine
.add(String.format("-Dgemfire.Query.VERBOSE=%1$s", GEMFIRE_QUERY_DEBUG));
commandLine.addAll(extractJvmArguments(args));
commandLine.add("-classpath");
commandLine.add(System.getProperty("java.class.path"));
@@ -156,11 +162,10 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static Process run(Class<?> type, File directory, String... args) throws IOException {
return new ProcessBuilder()
.command(createJavaProcessCommandLine(type, args))
.directory(directory)
.start();
protected static Process run(Class<?> type, File directory, String... args)
throws IOException {
return new ProcessBuilder().command(createJavaProcessCommandLine(type, args))
.directory(directory).start();
}
/* (non-Javadoc) */
@@ -169,8 +174,10 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(CacheServer cacheServer, long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(), cacheServer.getPort(), duration);
protected static boolean waitForCacheServerToStart(CacheServer cacheServer,
long duration) {
return waitForCacheServerToStart(cacheServer.getBindAddress(),
cacheServer.getPort(), duration);
}
/* (non-Javadoc) */
@@ -179,7 +186,8 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static boolean waitForCacheServerToStart(final String host, final int port, long duration) {
protected static boolean waitForCacheServerToStart(final String host, final int port,
long duration) {
return waitOnCondition(new Condition() {
AtomicBoolean connected = new AtomicBoolean(false);
@@ -203,7 +211,8 @@ public abstract class AbstractGemFireIntegrationTests {
}, duration);
}
// NOTE this method would not be necessary except Spring Sessions' build does not fork the test JVM
// NOTE this method would not be necessary except Spring Sessions' build does not fork
// the test JVM
// for every test class.
/* (non-Javadoc) */
protected static boolean waitForClientCacheToClose() {
@@ -238,7 +247,8 @@ public abstract class AbstractGemFireIntegrationTests {
/* (non-Javadoc) */
@SuppressWarnings("all")
protected static boolean waitForProcessToStart(Process process, File directory, long duration) {
protected static boolean waitForProcessToStart(Process process, File directory,
long duration) {
final File processControl = new File(directory, DEFAULT_PROCESS_CONTROL_FILENAME);
waitOnCondition(new Condition() {
@@ -256,7 +266,8 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected static int waitForProcessToStop(Process process, File directory, long duration) {
protected static int waitForProcessToStop(Process process, File directory,
long duration) {
final long timeout = (System.currentTimeMillis() + duration);
try {
@@ -311,24 +322,30 @@ public abstract class AbstractGemFireIntegrationTests {
}
/* (non-Javadoc) */
protected void assertRegion(Region<?, ?> actualRegion, String expectedName, DataPolicy expectedDataPolicy) {
protected void assertRegion(Region<?, ?> actualRegion, String expectedName,
DataPolicy expectedDataPolicy) {
assertThat(actualRegion).isNotNull();
assertThat(actualRegion.getName()).isEqualTo(expectedName);
assertThat(actualRegion.getFullPath()).isEqualTo(GemFireUtils.toRegionPath(expectedName));
assertThat(actualRegion.getFullPath())
.isEqualTo(GemFireUtils.toRegionPath(expectedName));
assertThat(actualRegion.getAttributes()).isNotNull();
assertThat(actualRegion.getAttributes().getDataPolicy()).isEqualTo(expectedDataPolicy);
assertThat(actualRegion.getAttributes().getDataPolicy())
.isEqualTo(expectedDataPolicy);
}
/* (non-Javadoc) */
protected void assertIndex(Index index, String expectedExpression, String expectedFromClause) {
protected void assertIndex(Index index, String expectedExpression,
String expectedFromClause) {
assertThat(index).isNotNull();
assertThat(index.getIndexedExpression()).isEqualTo(expectedExpression);
assertThat(index.getFromClause()).isEqualTo(expectedFromClause);
}
/* (non-Javadoc) */
protected void assertEntryIdleTimeout(Region<?, ?> region, ExpirationAction expectedAction, int expectedTimeout) {
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(), expectedAction, expectedTimeout);
protected void assertEntryIdleTimeout(Region<?, ?> region,
ExpirationAction expectedAction, int expectedTimeout) {
assertEntryIdleTimeout(region.getAttributes().getEntryIdleTimeout(),
expectedAction, expectedTimeout);
}
/* (non-Javadoc) */
@@ -403,13 +420,14 @@ public abstract class AbstractGemFireIntegrationTests {
}
/**
* The SessionEventListener class is a Spring {@link ApplicationListener} listening for Spring HTTP Session
* application events.
* The SessionEventListener class is a Spring {@link ApplicationListener} listening
* for Spring HTTP Session application events.
*
* @see org.springframework.context.ApplicationListener
* @see org.springframework.session.events.AbstractSessionEvent
*/
public static class SessionEventListener implements ApplicationListener<AbstractSessionEvent> {
public static class SessionEventListener
implements ApplicationListener<AbstractSessionEvent> {
private volatile AbstractSessionEvent sessionEvent;
@@ -439,7 +457,8 @@ public abstract class AbstractGemFireIntegrationTests {
}
/**
* The Condition interface defines a logical condition that must be satisfied before it is safe to proceed.
* The Condition interface defines a logical condition that must be satisfied before
* it is safe to proceed.
*/
protected interface Condition {
boolean evaluate();

View File

@@ -69,16 +69,19 @@ import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The ClientServerGemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test cases testing
* the functionality of GemFire-backed Spring Sessions using a GemFire client-server topology.
* The ClientServerGemFireOperationsSessionRepositoryIntegrationTests class is a test
* suite of test cases testing the functionality of GemFire-backed Spring Sessions using a
* GemFire client-server topology.
*
* @author John Blum
* @since 1.1.0
* @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
@@ -89,15 +92,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* @see com.gemstone.gemfire.cache.server.CacheServer
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =
ClientServerGemFireOperationsSessionRepositoryIntegrationTests.SpringSessionGemFireClientConfiguration.class)
@ContextConfiguration(classes = ClientServerGemFireOperationsSessionRepositoryIntegrationTests.SpringSessionGemFireClientConfiguration.class)
@DirtiesContext
@WebAppConfiguration
public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests
extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final DateFormat TIMESTAMP = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
private static final DateFormat TIMESTAMP = new SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss");
private static File processWorkingDirectory;
@@ -114,21 +118,25 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
final int port = SocketUtils.findAvailableTcpPort();
System.err.printf("Starting GemFire Server running on [%1$s] listening on port [%2$d]%n",
InetAddress.getLocalHost().getHostName(), port);
System.err.printf(
"Starting GemFire Server running on [%1$s] listening on port [%2$d]%n",
InetAddress.getLocalHost().getHostName(), port);
System.setProperty("spring.session.data.gemfire.port", String.valueOf(port));
String processWorkingDirectoryPathname = String.format("gemfire-client-server-tests-%1$s",
TIMESTAMP.format(new Date()));
String processWorkingDirectoryPathname = String
.format("gemfire-client-server-tests-%1$s", TIMESTAMP.format(new Date()));
processWorkingDirectory = createDirectory(processWorkingDirectoryPathname);
gemfireServer = run(SpringSessionGemFireServerConfiguration.class, processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
gemfireServer = run(SpringSessionGemFireServerConfiguration.class,
processWorkingDirectory,
String.format("-Dspring.session.data.gemfire.port=%1$d", port));
assertThat(waitForCacheServerToStart(SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
assertThat(waitForCacheServerToStart(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME, port)).isTrue();
System.err.printf("GemFire Server [startup time = %1$d ms]%n", System.currentTimeMillis() - t0);
System.err.printf("GemFire Server [startup time = %1$d ms]%n",
System.currentTimeMillis() - t0);
}
@AfterClass
@@ -136,10 +144,11 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
if (gemfireServer != null) {
gemfireServer.destroyForcibly();
System.err.printf("GemFire Server [exit code = %1$d]%n",
waitForProcessToStop(gemfireServer, processWorkingDirectory));
waitForProcessToStop(gemfireServer, processWorkingDirectory));
}
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean", Boolean.TRUE.toString()))) {
if (Boolean.valueOf(System.getProperty("spring.session.data.gemfire.fork.clean",
Boolean.TRUE.toString()))) {
FileSystemUtils.deleteRecursively(processWorkingDirectory);
}
@@ -150,16 +159,17 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
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);
assertThat(springSessionGemFireRegionAttributes.getDataPolicy())
.isEqualTo(DataPolicy.EMPTY);
}
@After
@@ -173,7 +183,8 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
ExpiringSession expectedSession = save(createSession());
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
AbstractSessionEvent sessionEvent = this.sessionEventListener
.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
@@ -181,9 +192,12 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.getId()).isNotNull();
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(createdSession.getCreationTime())
.isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime())
.isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.gemfireSessionRepository.delete(expectedSession.getId());
}
@@ -192,28 +206,32 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
public void getExistingNonExpiredSessionBeforeAndAfterExpiration() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
AbstractSessionEvent sessionEvent = this.sessionEventListener
.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
assertThat(this.sessionEventListener.getSessionEvent()).isNull();
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession savedSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
// NOTE for some reason or another, performing a GemFire (Client)Cache Region.get(key)
// NOTE for some reason or another, performing a GemFire (Client)Cache
// Region.get(key)
// causes a Region CREATE event... o.O
// calling sessionEventListener.getSessionEvent() here to clear the event
this.sessionEventListener.getSessionEvent();
sessionEvent = this.sessionEventListener.waitForSessionEvent(TimeUnit.SECONDS.toMillis(
MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
sessionEvent = this.sessionEventListener.waitForSessionEvent(
TimeUnit.SECONDS.toMillis(MAX_INACTIVE_INTERVAL_IN_SECONDS + 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession expiredSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession expiredSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(expiredSession).isNull();
}
@@ -222,7 +240,8 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
public void deleteExistingNonExpiredSessionFiresSessionDeletedEventAndReturnsNullOnGet() {
ExpiringSession expectedSession = save(touch(createSession()));
AbstractSessionEvent sessionEvent = this.sessionEventListener.waitForSessionEvent(500);
AbstractSessionEvent sessionEvent = this.sessionEventListener
.waitForSessionEvent(500);
assertThat(sessionEvent).isInstanceOf(SessionCreatedEvent.class);
assertThat(sessionEvent.<ExpiringSession>getSession()).isEqualTo(expectedSession);
@@ -234,13 +253,13 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSession.getId());
ExpiringSession deletedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession deletedSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(deletedSession).isNull();
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireClientConfiguration {
@Bean
@@ -251,13 +270,16 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", ClientServerGemFireOperationsSessionRepositoryIntegrationTests.class.getName());
gemfireProperties.setProperty("name",
ClientServerGemFireOperationsSessionRepositoryIntegrationTests.class
.getName());
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
return gemfireProperties;
}
@Bean(name = GemfireConstants.DEFAULT_GEMFIRE_POOL_NAME)
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean gemfirePool(@Value("${spring.session.data.gemfire.port:"
+ DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
PoolFactoryBean poolFactory = new PoolFactoryBean() {
@Override
protected Properties resolveGemfireProperties() {
@@ -268,15 +290,18 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
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
.setServerEndpoints(Collections.singletonList(new ConnectionEndpoint(
SpringSessionGemFireServerConfiguration.SERVER_HOSTNAME,
port)));
return poolFactory;
}
@@ -302,7 +327,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
@SuppressWarnings("resource")
public static void main(final String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(
SpringSessionGemFireClientConfiguration.class);
SpringSessionGemFireClientConfiguration.class);
applicationContext.registerShutdownHook();
@@ -310,13 +335,12 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
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());
}
}
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireServerConfiguration {
static final int MAX_CONNECTIONS = 50;
@@ -331,7 +355,8 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", SpringSessionGemFireServerConfiguration.class.getName());
gemfireProperties.setProperty("name",
SpringSessionGemFireServerConfiguration.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-file", "server.log");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
@@ -351,7 +376,8 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
@Bean
CacheServerFactoryBean gemfireCacheServer(Cache gemfireCache,
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT + "}") int port) {
@Value("${spring.session.data.gemfire.port:" + DEFAULT_GEMFIRE_SERVER_PORT
+ "}") int port) {
CacheServerFactoryBean cacheServerFactory = new CacheServerFactoryBean();
@@ -366,7 +392,8 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte
@SuppressWarnings("resource")
public static void main(final String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringSessionGemFireServerConfiguration.class);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SpringSessionGemFireServerConfiguration.class);
context.registerShutdownHook();
writeProcessControlFile(WORKING_DIRECTORY);
}

View File

@@ -55,8 +55,9 @@ import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The GemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test cases testing
* the findByPrincipalName query method on the GemFireOpeationsSessionRepository class.
* The GemFireOperationsSessionRepositoryIntegrationTests class is a test suite of test
* cases testing the findByPrincipalName query method on the
* GemFireOpeationsSessionRepository class.
*
* @author John Blum
* @since 1.1.0
@@ -75,7 +76,8 @@ import static org.assertj.core.api.Assertions.assertThat;
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireOperationsSessionRepositoryIntegrationTests extends AbstractGemFireIntegrationTests {
public class GemFireOperationsSessionRepositoryIntegrationTests
extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 300;
@@ -90,48 +92,63 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
@Before
public void setup() {
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext = SecurityContextHolder.createEmptyContext();
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken("changedContext-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
"changedContext-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
assertThat(this.gemfireCache).isNotNull();
assertThat(this.gemfireSessionRepository).isNotNull();
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache
.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME, DataPolicy.PARTITION);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME,
DataPolicy.PARTITION);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE,
MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
protected Map<String, ExpiringSession> doFindByIndexNameAndIndexValue(String indexName, String indexValue) {
return this.gemfireSessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
protected Map<String, ExpiringSession> doFindByIndexNameAndIndexValue(
String indexName, String indexValue) {
return this.gemfireSessionRepository.findByIndexNameAndIndexValue(indexName,
indexValue);
}
protected Map<String, ExpiringSession> doFindByPrincipalName(String principalName) {
return doFindByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
return doFindByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principalName);
}
@SuppressWarnings({ "unchecked" })
protected Map<String, ExpiringSession> doFindByPrincipalName(String regionName, String principalName) {
protected Map<String, ExpiringSession> doFindByPrincipalName(String regionName,
String principalName) {
try {
Region<String, ExpiringSession> region = this.gemfireCache.getRegion(regionName);
Region<String, ExpiringSession> region = this.gemfireCache
.getRegion(regionName);
assertThat(region).isNotNull();
QueryService queryService = region.getRegionService().getQueryService();
String queryString = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
region.getFullPath());
String queryString = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
region.getFullPath());
Query query = queryService.newQuery(queryString);
SelectResults<ExpiringSession> results = (SelectResults<ExpiringSession>) query.execute(
new Object[] { principalName });
SelectResults<ExpiringSession> results = (SelectResults<ExpiringSession>) query
.execute(new Object[] { principalName });
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(
results.size());
for (ExpiringSession session : results.asList()) {
sessions.put(session.getId(), session);
@@ -149,17 +166,22 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
return true;
}
protected ExpiringSession setAttribute(ExpiringSession session, String attributeName, Object attributeValue) {
protected ExpiringSession setAttribute(ExpiringSession session, String attributeName,
Object attributeValue) {
session.setAttribute(attributeName, attributeValue);
return session;
}
@Test
public void findSessionsByIndexedSessionAttributeNameValues() {
ExpiringSession johnBlumSession = save(touch(setAttribute(createSession("johnBlum"), "vip", "yes")));
ExpiringSession robWinchSession = save(touch(setAttribute(createSession("robWinch"), "vip", "yes")));
ExpiringSession jonDoeSession = save(touch(setAttribute(createSession("jonDoe"), "vip", "no")));
ExpiringSession pieDoeSession = save(touch(setAttribute(createSession("pieDoe"), "viper", "true")));
ExpiringSession johnBlumSession = save(
touch(setAttribute(createSession("johnBlum"), "vip", "yes")));
ExpiringSession robWinchSession = save(
touch(setAttribute(createSession("robWinch"), "vip", "yes")));
ExpiringSession jonDoeSession = save(
touch(setAttribute(createSession("jonDoe"), "vip", "no")));
ExpiringSession pieDoeSession = save(
touch(setAttribute(createSession("pieDoe"), "viper", "true")));
ExpiringSession sourDoeSession = save(touch(createSession("sourDoe")));
assertThat(get(johnBlumSession.getId())).isEqualTo(johnBlumSession);
@@ -173,7 +195,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
assertThat(get(sourDoeSession.getId())).isEqualTo(sourDoeSession);
assertThat(sourDoeSession.getAttributeNames().contains("vip")).isFalse();
Map<String, ExpiringSession> vipSessions = doFindByIndexNameAndIndexValue("vip", "yes");
Map<String, ExpiringSession> vipSessions = doFindByIndexNameAndIndexValue("vip",
"yes");
assertThat(vipSessions).isNotNull();
assertThat(vipSessions.size()).isEqualTo(2);
@@ -183,7 +206,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
assertThat(vipSessions.containsKey(pieDoeSession.getId()));
assertThat(vipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> nonVipSessions = doFindByIndexNameAndIndexValue("vip", "no");
Map<String, ExpiringSession> nonVipSessions = doFindByIndexNameAndIndexValue(
"vip", "no");
assertThat(nonVipSessions).isNotNull();
assertThat(nonVipSessions.size()).isEqualTo(1);
@@ -193,7 +217,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
assertThat(nonVipSessions.containsKey(pieDoeSession.getId()));
assertThat(nonVipSessions.containsKey(sourDoeSession.getId()));
Map<String, ExpiringSession> noSessions = doFindByIndexNameAndIndexValue("nonExistingAttribute", "test");
Map<String, ExpiringSession> noSessions = doFindByIndexNameAndIndexValue(
"nonExistingAttribute", "test");
assertThat(noSessions).isNotNull();
assertThat(noSessions.isEmpty()).isTrue();
@@ -241,7 +266,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
save(toSave);
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(getSecurityName());
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(
getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@@ -255,7 +281,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
save(toSave);
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(getSecurityName());
Map<String, ExpiringSession> findByPrincipalName = doFindByPrincipalName(
getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = doFindByPrincipalName(getChangedSecurityName());
@@ -264,7 +291,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
@Test
public void findsNoSessionsByNonExistingPrincipal() {
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName("nonExistingPrincipalName");
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName(
"nonExistingPrincipalName");
assertThat(nonExistingPrincipalSessions).isNotNull();
assertThat(nonExistingPrincipalSessions.isEmpty()).isTrue();
@@ -274,10 +302,12 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
public void findsNoSessionsAfterPrincipalIsRemoved() {
String username = "doesNotFindAfterPrincipalRemoved";
ExpiringSession session = save(touch(createSession(username)));
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, null);
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
null);
save(session);
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName(username);
Map<String, ExpiringSession> nonExistingPrincipalSessions = doFindByPrincipalName(
username);
assertThat(nonExistingPrincipalSessions).isNotNull();
assertThat(nonExistingPrincipalSessions.isEmpty()).isTrue();
@@ -287,12 +317,14 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
public void saveAndReadSessionWithAttributes() {
ExpiringSession expectedSession = this.gemfireSessionRepository.createSession();
assertThat(expectedSession).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(expectedSession).isInstanceOf(
AbstractGemFireOperationsSessionRepository.GemFireSession.class);
((AbstractGemFireOperationsSessionRepository.GemFireSession) expectedSession).setPrincipalName("jblum");
((AbstractGemFireOperationsSessionRepository.GemFireSession) expectedSession)
.setPrincipalName("jblum");
List<String> expectedAttributeNames = Arrays.asList(
"booleanAttribute", "numericAttribute", "stringAttribute", "personAttribute");
List<String> expectedAttributeNames = Arrays.asList("booleanAttribute",
"numericAttribute", "stringAttribute", "personAttribute");
Person jonDoe = new Person("Jon", "Doe");
@@ -303,21 +335,32 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
this.gemfireSessionRepository.save(touch(expectedSession));
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession savedSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
assertThat(savedSession).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(((AbstractGemFireOperationsSessionRepository.GemFireSession) savedSession).getPrincipalName()).isEqualTo("jblum");
assertThat(savedSession).isInstanceOf(
AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(
((AbstractGemFireOperationsSessionRepository.GemFireSession) savedSession)
.getPrincipalName()).isEqualTo("jblum");
assertThat(savedSession.getAttributeNames().containsAll(expectedAttributeNames)).as(
String.format("Expected (%1$s); but was (%2$s)", expectedAttributeNames, savedSession.getAttributeNames()))
assertThat(savedSession.getAttributeNames().containsAll(expectedAttributeNames))
.as(String.format("Expected (%1$s); but was (%2$s)",
expectedAttributeNames, savedSession.getAttributeNames()))
.isTrue();
assertThat(Boolean.valueOf(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(0))))).isTrue();
assertThat(Double.valueOf(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(1)))))
.isEqualTo(Math.PI);
assertThat(String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(2)))).isEqualTo("test");
assertThat(savedSession.getAttribute(expectedAttributeNames.get(3))).isEqualTo(jonDoe);
assertThat(Boolean.valueOf(
String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(0)))))
.isTrue();
assertThat(Double.valueOf(
String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(1)))))
.isEqualTo(Math.PI);
assertThat(
String.valueOf(savedSession.getAttribute(expectedAttributeNames.get(2))))
.isEqualTo("test");
assertThat(savedSession.getAttribute(expectedAttributeNames.get(3)))
.isEqualTo(jonDoe);
}
private String getSecurityName() {
@@ -328,15 +371,15 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
return this.changedContext.getAuthentication().getName();
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class SpringSessionGemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", GemFireOperationsSessionRepositoryIntegrationTests.class.getName());
gemfireProperties.setProperty("name",
GemFireOperationsSessionRepositoryIntegrationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);
@@ -370,7 +413,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
}
private String validate(String value) {
Assert.hasText(value, String.format("The String value (%1$s) must be specified!", value));
Assert.hasText(value,
String.format("The String value (%1$s) must be specified!", value));
return value;
}
@@ -399,7 +443,8 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
@SuppressWarnings("all")
public int compareTo(final Person person) {
int compareValue = getLastName().compareTo(person.getLastName());
return (compareValue != 0 ? compareValue : getFirstName().compareTo(person.getFirstName()));
return (compareValue != 0 ? compareValue
: getFirstName().compareTo(person.getFirstName()));
}
@Override
@@ -415,7 +460,7 @@ public class GemFireOperationsSessionRepositoryIntegrationTests extends Abstract
Person that = (Person) obj;
return ObjectUtils.nullSafeEquals(this.getFirstName(), that.getFirstName())
&& ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName());
&& ObjectUtils.nullSafeEquals(this.getLastName(), that.getLastName());
}
@Override

View File

@@ -47,8 +47,9 @@ import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The EnableGemFireHttpSessionEventsIntegrationTests class is a test suite of test cases testing the Session Event
* functionality and behavior of the GemFireOperationsSessionRepository and GemFire's configuration.
* The EnableGemFireHttpSessionEventsIntegrationTests class is a test suite of test cases
* testing the Session Event functionality and behavior of the
* GemFireOperationsSessionRepository and GemFire's configuration.
*
* @author John Blum
* @since 1.1.0
@@ -70,7 +71,8 @@ import static org.assertj.core.api.Assertions.assertThat;
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemFireIntegrationTests {
public class EnableGemFireHttpSessionEventsIntegrationTests
extends AbstractGemFireIntegrationTests {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
@@ -84,14 +86,17 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
public void setup() {
assertThat(GemFireUtils.isPeer(this.gemfireCache)).isTrue();
assertThat(this.gemfireSessionRepository).isNotNull();
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(
MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.sessionEventListener).isNotNull();
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
Region<Object, ExpiringSession> sessionRegion = this.gemfireCache
.getRegion(SPRING_SESSION_GEMFIRE_REGION_NAME);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME, DataPolicy.REPLICATE);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE, MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertRegion(sessionRegion, SPRING_SESSION_GEMFIRE_REGION_NAME,
DataPolicy.REPLICATE);
assertEntryIdleTimeout(sessionRegion, ExpirationAction.INVALIDATE,
MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@After
@@ -113,9 +118,12 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.getId()).isNotNull();
assertThat(createdSession.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(createdSession.getCreationTime())
.isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(createdSession.getLastAccessedTime())
.isEqualTo(createdSession.getCreationTime());
assertThat(createdSession.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(createdSession.isExpired()).isFalse();
}
@@ -126,7 +134,8 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(expectedSession.isExpired()).isFalse();
// NOTE though unlikely, a possible race condition exists between save and get...
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession savedSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
}
@@ -143,18 +152,21 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(createdSession).isEqualTo(expectedSession);
assertThat(createdSession.isExpired()).isTrue();
assertThat(this.gemfireSessionRepository.getSession(createdSession.getId())).isNull();
assertThat(this.gemfireSessionRepository.getSession(createdSession.getId()))
.isNull();
}
@Test
public void getNonExistingSession() {
assertThat(this.gemfireSessionRepository.getSession(UUID.randomUUID().toString())).isNull();
assertThat(this.gemfireSessionRepository.getSession(UUID.randomUUID().toString()))
.isNull();
}
@Test
public void deleteExistingNonExpiredSession() {
ExpiringSession expectedSession = save(touch(createSession()));
ExpiringSession savedSession = this.gemfireSessionRepository.getSession(expectedSession.getId());
ExpiringSession savedSession = this.gemfireSessionRepository
.getSession(expectedSession.getId());
assertThat(savedSession).isEqualTo(expectedSession);
assertThat(savedSession.isExpired()).isFalse();
@@ -169,7 +181,8 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
ExpiringSession deletedSession = sessionEvent.getSession();
assertThat(deletedSession).isEqualTo(savedSession);
assertThat(this.gemfireSessionRepository.getSession(deletedSession.getId())).isNull();
assertThat(this.gemfireSessionRepository.getSession(deletedSession.getId()))
.isNull();
}
@Test
@@ -184,8 +197,9 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(createdSession).isEqualTo(expectedSession);
sessionEvent = this.sessionEventListener.waitForSessionEvent(TimeUnit.SECONDS.toMillis(
this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds() + 1));
sessionEvent = this.sessionEventListener.waitForSessionEvent(TimeUnit.SECONDS
.toMillis(this.gemfireSessionRepository.getMaxInactiveIntervalInSeconds()
+ 1));
assertThat(sessionEvent).isInstanceOf(SessionExpiredEvent.class);
@@ -201,7 +215,8 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(sessionEvent).isInstanceOf(SessionDeletedEvent.class);
assertThat(sessionEvent.getSession()).isNull();
assertThat(sessionEvent.getSessionId()).isEqualTo(expiredSession.getId());
assertThat(this.gemfireSessionRepository.getSession(sessionEvent.getSessionId())).isNull();
assertThat(this.gemfireSessionRepository.getSession(sessionEvent.getSessionId()))
.isNull();
}
@Test
@@ -219,16 +234,15 @@ public class EnableGemFireHttpSessionEventsIntegrationTests extends AbstractGemF
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
}
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME,
maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS,
serverRegionShortcut = RegionShortcut.REPLICATE)
@EnableGemFireHttpSession(regionName = SPRING_SESSION_GEMFIRE_REGION_NAME, maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS, serverRegionShortcut = RegionShortcut.REPLICATE)
static class SpringSessionGemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", EnableGemFireHttpSessionEventsIntegrationTests.class.getName());
gemfireProperties.setProperty("name",
EnableGemFireHttpSessionEventsIntegrationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", GEMFIRE_LOG_LEVEL);

View File

@@ -42,8 +42,9 @@ import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The GemFireHttpSessionJavaConfigurationTests class is a test suite of test cases testing the configuration of
* Spring Session backed by GemFire using Java-based configuration meta-data.
* The GemFireHttpSessionJavaConfigurationTests class is a test suite of test cases
* testing the configuration of Spring Session backed by GemFire using Java-based
* configuration meta-data.
*
* @author John Blum
* @since 1.1.0
@@ -61,12 +62,14 @@ import static org.assertj.core.api.Assertions.assertThat;
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireHttpSessionJavaConfigurationTests extends AbstractGemFireIntegrationTests {
public class GemFireHttpSessionJavaConfigurationTests
extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache, String regionName, DataPolicy dataPolicy) {
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache,
String regionName, DataPolicy dataPolicy) {
assertThat(GemFireUtils.isPeer(gemfireCache)).isTrue();
Region<K, V> region = gemfireCache.getRegion(regionName);
@@ -78,16 +81,16 @@ public class GemFireHttpSessionJavaConfigurationTests extends AbstractGemFireInt
@Test
public void gemfireCacheConfigurationIsValid() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "JavaExample",
DataPolicy.REPLICATE);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"JavaExample", DataPolicy.REPLICATE);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 900);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "JavaExample",
DataPolicy.REPLICATE);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"JavaExample", DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
@@ -100,26 +103,27 @@ public class GemFireHttpSessionJavaConfigurationTests extends AbstractGemFireInt
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasNotCreated() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "JavaExample",
DataPolicy.REPLICATE);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"JavaExample", DataPolicy.REPLICATE);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
Index sessionAttributesIndex = queryService.getIndex(example,
"sessionAttributesIndex");
assertThat(sessionAttributesIndex).isNull();
}
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900,
regionName = "JavaExample", serverRegionShortcut = RegionShortcut.REPLICATE)
@EnableGemFireHttpSession(indexableSessionAttributes = {}, maxInactiveIntervalInSeconds = 900, regionName = "JavaExample", serverRegionShortcut = RegionShortcut.REPLICATE)
public static class GemFireConfiguration {
@Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", GemFireHttpSessionJavaConfigurationTests.class.getName());
gemfireProperties.setProperty("name",
GemFireHttpSessionJavaConfigurationTests.class.getName());
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "warning");
return gemfireProperties;

View File

@@ -37,8 +37,9 @@ import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* The GemFireHttpSessionXmlConfigurationTests class is a test suite of test cases testing the configuration of
* Spring Session backed by GemFire using XML configuration meta-data.
* The GemFireHttpSessionXmlConfigurationTests class is a test suite of test cases testing
* the configuration of Spring Session backed by GemFire using XML configuration
* meta-data.
*
* @author John Blum
* @since 1.1.0
@@ -56,12 +57,14 @@ import static org.assertj.core.api.Assertions.assertThat;
@ContextConfiguration
@DirtiesContext
@WebAppConfiguration
public class GemFireHttpSessionXmlConfigurationTests extends AbstractGemFireIntegrationTests {
public class GemFireHttpSessionXmlConfigurationTests
extends AbstractGemFireIntegrationTests {
@Autowired
private Cache gemfireCache;
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache, String regionName, DataPolicy dataPolicy) {
protected <K, V> Region<K, V> assertCacheAndRegion(Cache gemfireCache,
String regionName, DataPolicy dataPolicy) {
assertThat(GemFireUtils.isPeer(gemfireCache)).isTrue();
Region<K, V> region = gemfireCache.getRegion(regionName);
@@ -73,14 +76,16 @@ public class GemFireHttpSessionXmlConfigurationTests extends AbstractGemFireInte
@Test
public void gemfireCacheConfigurationIsValid() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"XmlExample", DataPolicy.NORMAL);
assertEntryIdleTimeout(example, ExpirationAction.INVALIDATE, 3600);
}
@Test
public void verifyGemFireExampleCacheRegionPrincipalNameIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"XmlExample", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
@@ -93,16 +98,18 @@ public class GemFireHttpSessionXmlConfigurationTests extends AbstractGemFireInte
@Test
public void verifyGemFireExampleCacheRegionSessionAttributesIndexWasCreatedSuccessfully() {
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache, "XmlExample", DataPolicy.NORMAL);
Region<Object, ExpiringSession> example = assertCacheAndRegion(this.gemfireCache,
"XmlExample", DataPolicy.NORMAL);
QueryService queryService = example.getRegionService().getQueryService();
assertThat(queryService).isNotNull();
Index sessionAttributesIndex = queryService.getIndex(example, "sessionAttributesIndex");
Index sessionAttributesIndex = queryService.getIndex(example,
"sessionAttributesIndex");
assertIndex(sessionAttributesIndex, "s.attributes['one', 'two', 'three']",
String.format("%1$s s", example.getFullPath()));
String.format("%1$s s", example.getFullPath()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -15,8 +15,14 @@
*/
package org.springframework.session.data.mongo;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import com.mongodb.MongoClient;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -33,11 +39,6 @@ import org.springframework.session.data.AbstractITests;
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;
import org.springframework.test.context.ContextConfiguration;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -57,69 +58,71 @@ public class MongoRepositoryITests extends AbstractITests {
public void saves() throws InterruptedException {
String username = "saves-" + System.currentTimeMillis();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
toSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username, "password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, toSaveContext);
toSave.setAttribute(INDEX_NAME, username);
repository.save(toSave);
this.repository.save(toSave);
Session session = repository.getSession(toSave.getId());
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getId()).isEqualTo(toSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(toSave.getAttribute(expectedAttributeName));
assertThat(session.getAttribute(expectedAttributeName))
.isEqualTo(toSave.getAttribute(expectedAttributeName));
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
String id = toSave.getId();
assertThat(repository.getSession(id)).isNull();
assertThat(this.repository.getSession(id)).isNull();
}
@Test
public void putAllOnSingleAttrDoesNotRemoveOld() {
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute("a", "b");
repository.save(toSave);
toSave = repository.getSession(toSave.getId());
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("1", "2");
repository.save(toSave);
toSave = repository.getSession(toSave.getId());
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
Session session = repository.getSession(toSave.getId());
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getAttributeNames().size()).isEqualTo(2);
assertThat(session.getAttribute("a")).isEqualTo("b");
assertThat(session.getAttribute("1")).isEqualTo("2");
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
}
@Test
public void findByPrincipalName() throws Exception {
String principalName = "findByPrincipalName" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -127,17 +130,18 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChange" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChange"
+ UUID.randomUUID();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -145,19 +149,20 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
+ UUID.randomUUID();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave = repository.getSession(toSave.getId());
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -166,16 +171,16 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByDeletedPrincipalName() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, null);
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -184,19 +189,20 @@ public class MongoRepositoryITests extends AbstractITests {
public void findByChangedPrincipalName() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, principalNameChanged);
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -205,17 +211,17 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByDeletedPrincipalNameReload() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
MongoExpiringSession getSession = repository.getSession(toSave.getId());
MongoExpiringSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
repository.save(getSession);
this.repository.save(getSession);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -224,21 +230,22 @@ public class MongoRepositoryITests extends AbstractITests {
public void findByChangedPrincipalNameReload() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
MongoExpiringSession toSave = repository.createSession();
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
MongoExpiringSession getSession = repository.getSession(toSave.getId());
MongoExpiringSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, principalNameChanged);
repository.save(getSession);
this.repository.save(getSession);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -246,20 +253,21 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findBySecurityPrincipalName() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -267,35 +275,36 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
throws Exception {
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave = repository.getSession(toSave.getId());
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -303,35 +312,36 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByDeletedSecurityPrincipalName() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, null);
repository.save(toSave);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalName() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, changedContext);
repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(toSave);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -339,38 +349,39 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
MongoExpiringSession getSession = repository.getSession(toSave.getId());
MongoExpiringSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
repository.save(getSession);
this.repository.save(getSession);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalNameReload() throws Exception {
MongoExpiringSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
MongoExpiringSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
MongoExpiringSession getSession = repository.getSession(toSave.getId());
MongoExpiringSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(SPRING_SECURITY_CONTEXT, changedContext);
repository.save(getSession);
getSession.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(getSession);
Map<String, MongoExpiringSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, MongoExpiringSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -378,23 +389,25 @@ public class MongoRepositoryITests extends AbstractITests {
@Test
public void loadExpiredSession() throws Exception {
//given
MongoExpiringSession expiredSession = repository.createSession();
long thirtyOneMinutesAgo = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(31);
// given
MongoExpiringSession expiredSession = this.repository.createSession();
long thirtyOneMinutesAgo = System.currentTimeMillis()
- TimeUnit.MINUTES.toMillis(31);
expiredSession.setLastAccessedTime(thirtyOneMinutesAgo);
repository.save(expiredSession);
this.repository.save(expiredSession);
//then
MongoExpiringSession expiredSessionFromDb = repository.getSession(expiredSession.getId());
// then
MongoExpiringSession expiredSessionFromDb = this.repository
.getSession(expiredSession.getId());
assertThat(expiredSessionFromDb).isNull();
}
private String getSecurityName() {
return context.getAuthentication().getName();
return this.context.getAuthentication().getName();
}
private String getChangedSecurityName() {
return changedContext.getAuthentication().getName();
return this.changedContext.getAuthentication().getName();
}
@Configuration
@@ -407,4 +420,4 @@ public class MongoRepositoryITests extends AbstractITests {
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2014-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.
@@ -15,12 +15,11 @@
*/
package org.springframework.session.data.redis;
import static org.assertj.core.api.Assertions.*;
import java.util.Map;
import java.util.UUID;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,6 +41,8 @@ import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.test.context.ContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
@ContextConfiguration
public class RedisOperationsSessionRepositoryITests extends AbstractITests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@@ -58,86 +59,90 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
public void saves() throws InterruptedException {
String username = "saves-" + System.currentTimeMillis();
String usernameSessionKey = "spring:session:RedisOperationsSessionRepositoryITests:index:" + INDEX_NAME + ":"
+ username;
String usernameSessionKey = "spring:session:RedisOperationsSessionRepositoryITests:index:"
+ INDEX_NAME + ":" + username;
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
toSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username, "password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, toSaveContext);
toSave.setAttribute(INDEX_NAME, username);
registry.clear();
this.registry.clear();
repository.save(toSave);
this.repository.save(toSave);
assertThat(registry.receivedEvent(toSave.getId())).isTrue();
assertThat(registry.getEvent(toSave.getId())).isInstanceOf(SessionCreatedEvent.class);
assertThat(redis.boundSetOps(usernameSessionKey).members()).contains(toSave.getId());
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
assertThat(this.registry.getEvent(toSave.getId()))
.isInstanceOf(SessionCreatedEvent.class);
assertThat(this.redis.boundSetOps(usernameSessionKey).members())
.contains(toSave.getId());
Session session = repository.getSession(toSave.getId());
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getId()).isEqualTo(toSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(toSave.getAttribute(expectedAttributeName));
assertThat(session.getAttribute(expectedAttributeName))
.isEqualTo(toSave.getAttribute(expectedAttributeName));
registry.clear();
this.registry.clear();
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
assertThat(repository.getSession(toSave.getId())).isNull();
assertThat(registry.getEvent(toSave.getId())).isInstanceOf(SessionDestroyedEvent.class);
assertThat(redis.boundSetOps(usernameSessionKey).members()).doesNotContain(toSave.getId());
assertThat(this.repository.getSession(toSave.getId())).isNull();
assertThat(this.registry.getEvent(toSave.getId()))
.isInstanceOf(SessionDestroyedEvent.class);
assertThat(this.redis.boundSetOps(usernameSessionKey).members())
.doesNotContain(toSave.getId());
assertThat(registry.getEvent(toSave.getId()).getSession().getAttribute(expectedAttributeName))
.isEqualTo(expectedAttributeValue);
assertThat(this.registry.getEvent(toSave.getId()).getSession()
.getAttribute(expectedAttributeName)).isEqualTo(expectedAttributeValue);
}
@Test
public void putAllOnSingleAttrDoesNotRemoveOld() {
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute("a", "b");
repository.save(toSave);
toSave = repository.getSession(toSave.getId());
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("1", "2");
repository.save(toSave);
toSave = repository.getSession(toSave.getId());
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
Session session = repository.getSession(toSave.getId());
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getAttributeNames().size()).isEqualTo(2);
assertThat(session.getAttribute("a")).isEqualTo("b");
assertThat(session.getAttribute("1")).isEqualTo("2");
repository.delete(toSave.getId());
this.repository.delete(toSave.getId());
}
@Test
public void findByPrincipalName() throws Exception {
String principalName = "findByPrincipalName" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
repository.delete(toSave.getId());
assertThat(registry.receivedEvent(toSave.getId())).isTrue();
this.repository.delete(toSave.getId());
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -145,20 +150,23 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
String principalName = "findByPrincipalNameExpireRemovesIndex" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
String principalName = "findByPrincipalNameExpireRemovesIndex"
+ UUID.randomUUID();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:" + toSave.getId();
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:"
+ toSave.getId();
String channel = ":expired";
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
body.getBytes("UTF-8"));
byte[] pattern = new byte[] {};
repository.onMessage(message, pattern);
this.repository.onMessage(message, pattern);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -166,17 +174,18 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChange" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChange"
+ UUID.randomUUID();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -184,19 +193,20 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
+ UUID.randomUUID();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave = repository.getSession(toSave.getId());
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -205,16 +215,16 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByDeletedPrincipalName() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, null);
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -223,19 +233,20 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
public void findByChangedPrincipalName() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, principalNameChanged);
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -244,17 +255,17 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByDeletedPrincipalNameReload() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
RedisSession getSession = repository.getSession(toSave.getId());
RedisSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
repository.save(getSession);
this.repository.save(getSession);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -263,21 +274,22 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
public void findByChangedPrincipalNameReload() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
RedisSession toSave = repository.createSession();
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
repository.save(toSave);
this.repository.save(toSave);
RedisSession getSession = repository.getSession(toSave.getId());
RedisSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, principalNameChanged);
repository.save(getSession);
this.repository.save(getSession);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -285,21 +297,22 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findBySecurityPrincipalName() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
repository.delete(toSave.getId());
assertThat(registry.receivedEvent(toSave.getId())).isTrue();
this.repository.delete(toSave.getId());
assertThat(this.registry.receivedEvent(toSave.getId())).isTrue();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -307,19 +320,21 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:" + toSave.getId();
String body = "spring:session:RedisOperationsSessionRepositoryITests:sessions:expires:"
+ toSave.getId();
String channel = ":expired";
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body.getBytes("UTF-8"));
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"),
body.getBytes("UTF-8"));
byte[] pattern = new byte[] {};
repository.onMessage(message, pattern);
this.repository.onMessage(message, pattern);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -327,35 +342,36 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
throws Exception {
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave = repository.getSession(toSave.getId());
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -363,35 +379,36 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByDeletedSecurityPrincipalName() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, null);
repository.save(toSave);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalName() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, changedContext);
repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(toSave);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -399,49 +416,50 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
@Test
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
RedisSession getSession = repository.getSession(toSave.getId());
RedisSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
repository.save(getSession);
this.repository.save(getSession);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalNameReload() throws Exception {
RedisSession toSave = repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, context);
RedisSession toSave = this.repository.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
repository.save(toSave);
this.repository.save(toSave);
RedisSession getSession = repository.getSession(toSave.getId());
RedisSession getSession = this.repository.getSession(toSave.getId());
getSession.setAttribute(SPRING_SECURITY_CONTEXT, changedContext);
repository.save(getSession);
getSession.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(getSession);
Map<String, RedisSession> findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
Map<String, RedisSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
private String getSecurityName() {
return context.getAuthentication().getName();
return this.context.getAuthentication().getName();
}
private String getChangedSecurityName() {
return changedContext.getAuthentication().getName();
return this.changedContext.getAuthentication().getName();
}
@Configuration
@@ -459,4 +477,4 @@ public class RedisOperationsSessionRepositoryITests extends AbstractITests {
return new SessionEventRegistry();
}
}
}
}

View File

@@ -62,7 +62,8 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Expirin
public void expireFiresSessionExpiredEvent() throws InterruptedException {
S toSave = this.repository.createSession();
toSave.setAttribute("a", "b");
Authentication toSaveToken = new UsernamePasswordAuthenticationToken("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication toSaveToken = new UsernamePasswordAuthenticationToken("user",
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
toSave.setAttribute("SPRING_SECURITY_CONTEXT", toSaveContext);
@@ -86,7 +87,8 @@ public class EnableRedisHttpSessionExpireSessionDestroyedTests<S extends Expirin
assertThat(this.registry.receivedEvent()).isTrue();
}
static class SessionExpiredEventRegistry implements ApplicationListener<SessionExpiredEvent> {
static class SessionExpiredEventRegistry
implements ApplicationListener<SessionExpiredEvent> {
private boolean receivedEvent;
private Object lock;

View File

@@ -53,9 +53,10 @@ public class RedisListenerContainerTaskExecutorITests {
RedisOperations<Object, Object> redis;
@Test
public void testRedisDelEventsAreDispatchedInSessionTaskExecutor() throws InterruptedException {
BoundSetOperations<Object, Object> ops = this.redis
.boundSetOps("spring:session:RedisListenerContainerTaskExecutorITests:expirations:dummy");
public void testRedisDelEventsAreDispatchedInSessionTaskExecutor()
throws InterruptedException {
BoundSetOperations<Object, Object> ops = this.redis.boundSetOps(
"spring:session:RedisListenerContainerTaskExecutorITests:expirations:dummy");
ops.add("value");
ops.remove("value");
assertThat(this.executor.taskDispatched()).isTrue();

View File

@@ -33,8 +33,8 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.SocketUtils;
/**
* Integration tests that check the underlying data source - in this case
* Hazelcast Client.
* Integration tests that check the underlying data source - in this case Hazelcast
* Client.
*
* @author Vedran Pavic
* @author Artem Bilan
@@ -50,7 +50,6 @@ public class HazelcastClientRepositoryITests<S extends ExpiringSession>
private static HazelcastInstance hazelcastInstance;
@BeforeClass
public static void setup() {
hazelcastInstance = HazelcastITestUtils.embeddedHazelcastServer(PORT);
@@ -70,8 +69,7 @@ public class HazelcastClientRepositoryITests<S extends ExpiringSession>
@Bean
public HazelcastInstance embeddedHazelcastClient() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.getNetworkConfig()
.addAddress("127.0.0.1:" + PORT);
clientConfig.getNetworkConfig().addAddress("127.0.0.1:" + PORT);
return HazelcastClient.newHazelcastClient(clientConfig);
}

View File

@@ -39,8 +39,7 @@ public final class HazelcastITestUtils {
*/
public static HazelcastInstance embeddedHazelcastServer(int port) {
Config config = new Config();
config.getNetworkConfig()
.setPort(port);
config.getNetworkConfig().setPort(port);
return Hazelcast.newHazelcastInstance(config);
}

View File

@@ -28,8 +28,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* Integration tests that check the underlying data source - in this case
* Hazelcast Server.
* Integration tests that check the underlying data source - in this case Hazelcast
* Server.
*
* @author Tommy Ludwig
* @author Vedran Pavic

View File

@@ -45,9 +45,9 @@ import org.springframework.test.context.web.WebAppConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Ensure that the appropriate SessionEvents are fired at the expected times.
* Additionally ensure that the interactions with the {@link SessionRepository}
* abstraction behave as expected after each SessionEvent.
* Ensure that the appropriate SessionEvents are fired at the expected times. Additionally
* ensure that the interactions with the {@link SessionRepository} abstraction behave as
* expected after each SessionEvent.
*
* @author Tommy Ludwig
*/
@@ -78,22 +78,27 @@ public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
sessionToSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username, "password", AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
sessionToSave.setAttribute("SPRING_SECURITY_CONTEXT", toSaveContext);
sessionToSave.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
sessionToSave.setAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
this.repository.save(sessionToSave);
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
assertThat(this.registry.getEvent(sessionToSave.getId())).isInstanceOf(SessionCreatedEvent.class);
assertThat(this.registry.getEvent(sessionToSave.getId()))
.isInstanceOf(SessionCreatedEvent.class);
Session session = this.repository.getSession(sessionToSave.getId());
assertThat(session.getId()).isEqualTo(sessionToSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(sessionToSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(sessionToSave.getAttribute(expectedAttributeName));
assertThat(session.getAttributeNames())
.isEqualTo(sessionToSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName))
.isEqualTo(sessionToSave.getAttribute(expectedAttributeName));
}
@Test
@@ -103,13 +108,16 @@ public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
this.repository.save(sessionToSave);
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
assertThat(this.registry.getEvent(sessionToSave.getId())).isInstanceOf(SessionCreatedEvent.class);
assertThat(this.registry.getEvent(sessionToSave.getId()))
.isInstanceOf(SessionCreatedEvent.class);
this.registry.clear();
assertThat(sessionToSave.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(sessionToSave.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
assertThat(this.registry.getEvent(sessionToSave.getId())).isInstanceOf(SessionExpiredEvent.class);
assertThat(this.registry.getEvent(sessionToSave.getId()))
.isInstanceOf(SessionExpiredEvent.class);
assertThat(this.repository.getSession(sessionToSave.getId())).isNull();
}
@@ -121,13 +129,15 @@ public class EnableHazelcastHttpSessionEventsTests<S extends ExpiringSession> {
this.repository.save(sessionToSave);
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
assertThat(this.registry.getEvent(sessionToSave.getId())).isInstanceOf(SessionCreatedEvent.class);
assertThat(this.registry.getEvent(sessionToSave.getId()))
.isInstanceOf(SessionCreatedEvent.class);
this.registry.clear();
this.repository.delete(sessionToSave.getId());
assertThat(this.registry.receivedEvent(sessionToSave.getId())).isTrue();
assertThat(this.registry.getEvent(sessionToSave.getId())).isInstanceOf(SessionDeletedEvent.class);
assertThat(this.registry.getEvent(sessionToSave.getId()))
.isInstanceOf(SessionDeletedEvent.class);
assertThat(this.repository.getSession(sessionToSave.getId())).isNull();
}

View File

@@ -37,8 +37,8 @@ import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test the different configuration options for the
* {@link EnableHazelcastHttpSession} annotation.
* Test the different configuration options for the {@link EnableHazelcastHttpSession}
* annotation.
*
* @author Tommy Ludwig
*/

View File

@@ -73,24 +73,27 @@ public class JdbcOperationsSessionRepositoryITests {
@Before
public void setup() throws Exception {
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(new UsernamePasswordAuthenticationToken(
"username-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext = SecurityContextHolder.createEmptyContext();
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
"changedContext-" + UUID.randomUUID(), "na", AuthorityUtils.createAuthorityList("ROLE_USER")));
"changedContext-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
}
@Test
public void saves() throws InterruptedException {
String username = "saves-" + System.currentTimeMillis();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
toSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username, "password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, toSaveContext);
@@ -102,7 +105,8 @@ public class JdbcOperationsSessionRepositoryITests {
assertThat(session.getId()).isEqualTo(toSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
assertThat(session.getAttribute(expectedAttributeName)).isEqualTo(toSave.getAttribute(expectedAttributeName));
assertThat(session.getAttribute(expectedAttributeName))
.isEqualTo(toSave.getAttribute(expectedAttributeName));
this.repository.delete(toSave.getId());
@@ -111,7 +115,8 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void putAllOnSingleAttrDoesNotRemoveOld() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute("a", "b");
this.repository.save(toSave);
@@ -133,20 +138,22 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByPrincipalName() throws Exception {
String principalName = "findByPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -154,17 +161,19 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
String principalName = "findByPrincipalNameExpireRemovesIndex" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
String principalName = "findByPrincipalNameExpireRemovesIndex"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
toSave.setLastAccessedTime(System.currentTimeMillis() -
(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
toSave.setLastAccessedTime(System.currentTimeMillis()
- (MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
this.repository.save(toSave);
this.repository.cleanUpExpiredSessions();
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -172,8 +181,10 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChange" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChange"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
@@ -181,8 +192,8 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -190,8 +201,10 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
@@ -201,8 +214,8 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -211,7 +224,8 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByDeletedPrincipalName() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
@@ -219,8 +233,8 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute(INDEX_NAME, null);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -229,7 +243,8 @@ public class JdbcOperationsSessionRepositoryITests {
public void findByChangedPrincipalName() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
@@ -237,11 +252,12 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute(INDEX_NAME, principalNameChanged);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -250,17 +266,19 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByDeletedPrincipalNameReload() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository.getSession(toSave.getId());
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@@ -269,21 +287,24 @@ public class JdbcOperationsSessionRepositoryITests {
public void findByChangedPrincipalNameReload() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository.getSession(toSave.getId());
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, principalNameChanged);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, principalNameChanged);
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -291,20 +312,22 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findBySecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -312,16 +335,17 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
toSave.setLastAccessedTime(System.currentTimeMillis() -
(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
toSave.setLastAccessedTime(System.currentTimeMillis()
- (MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
this.repository.save(toSave);
this.repository.cleanUpExpiredSessions();
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
@@ -329,7 +353,8 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
@@ -337,16 +362,18 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
@@ -356,8 +383,8 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -365,7 +392,8 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByDeletedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
@@ -373,15 +401,16 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute(SPRING_SECURITY_CONTEXT, null);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
@@ -389,11 +418,12 @@ public class JdbcOperationsSessionRepositoryITests {
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -401,38 +431,43 @@ public class JdbcOperationsSessionRepositoryITests {
@Test
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository.getSession(toSave.getId());
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository.getSession(toSave.getId());
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
repository.save(getSession);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName =
this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
@@ -452,10 +487,8 @@ public class JdbcOperationsSessionRepositoryITests {
@Bean
public EmbeddedDatabase dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("org/springframework/session/jdbc/schema-h2.sql")
.build();
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
.addScript("org/springframework/session/jdbc/schema-h2.sql").build();
}
@Bean

View File

@@ -17,7 +17,8 @@
package org.springframework.session;
/**
* A {@link Session} that contains additional attributes that are useful for determining if a session is expired.
* A {@link Session} that contains additional attributes that are useful for determining
* if a session is expired.
*
* @author Rob Winch
* @since 1.0
@@ -25,37 +26,47 @@ package org.springframework.session;
public interface ExpiringSession extends Session {
/**
* Gets the time when this session was created in milliseconds since midnight of 1/1/1970 GMT.
* Gets the time when this session was created in milliseconds since midnight of
* 1/1/1970 GMT.
*
* @return the time when this session was created in milliseconds since midnight of 1/1/1970 GMT.
* @return the time when this session was created in milliseconds since midnight of
* 1/1/1970 GMT.
*/
long getCreationTime();
/**
* Sets the last accessed time in milliseconds since midnight of 1/1/1970 GMT.
*
* @param lastAccessedTime the last accessed time in milliseconds since midnight of 1/1/1970 GMT
* @param lastAccessedTime the last accessed time in milliseconds since midnight of
* 1/1/1970 GMT
*/
void setLastAccessedTime(long lastAccessedTime);
/**
* Gets the last time this {@link Session} was accessed expressed in milliseconds since midnight of 1/1/1970 GMT.
* Gets the last time this {@link Session} was accessed expressed in milliseconds
* since midnight of 1/1/1970 GMT.
*
* @return the last time the client sent a request associated with the session expressed in milliseconds since midnight of 1/1/1970 GMT
* @return the last time the client sent a request associated with the session
* expressed in milliseconds since midnight of 1/1/1970 GMT
*/
long getLastAccessedTime();
/**
* Sets the maximum inactive interval in seconds between requests before this session will be invalidated. A negative time indicates that the session will never timeout.
* Sets the maximum inactive interval in seconds between requests before this session
* will be invalidated. A negative time indicates that the session will never timeout.
*
* @param interval the number of seconds that the {@link Session} should be kept alive between client requests.
* @param interval the number of seconds that the {@link Session} should be kept alive
* between client requests.
*/
void setMaxInactiveIntervalInSeconds(int interval);
/**
* Gets the maximum inactive interval in seconds between requests before this session will be invalidated. A negative time indicates that the session will never timeout.
* Gets the maximum inactive interval in seconds between requests before this session
* will be invalidated. A negative time indicates that the session will never timeout.
*
* @return the maximum inactive interval in seconds between requests before this session will be invalidated. A negative time indicates that the session will never timeout.
* @return the maximum inactive interval in seconds between requests before this
* session will be invalidated. A negative time indicates that the session will never
* timeout.
*/
int getMaxInactiveIntervalInSeconds();

View File

@@ -19,16 +19,16 @@ package org.springframework.session;
import java.util.Map;
/**
* Extends a basic {@link SessionRepository} to allow finding a session id by
* the principal name. The principal name is defined by the {@link Session}
* attribute with the name {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}.
* Extends a basic {@link SessionRepository} to allow finding a session id by the
* principal name. The principal name is defined by the {@link Session} attribute with the
* name {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}.
*
* @param <S>
* the type of Session being managed by this
* {@link FindByIndexNameSessionRepository}
* @param <S> the type of Session being managed by this
* {@link FindByIndexNameSessionRepository}
* @author Rob Winch
*/
public interface FindByIndexNameSessionRepository<S extends Session> extends SessionRepository<S> {
public interface FindByIndexNameSessionRepository<S extends Session>
extends SessionRepository<S> {
/**
* <p>
@@ -37,28 +37,27 @@ public interface FindByIndexNameSessionRepository<S extends Session> extends Ses
* </p>
*
* <p>
* It is the responsibility of the developer to ensure the attribute
* is populated since Spring Session is not aware of the authentication
* mechanism being used.
* It is the responsibility of the developer to ensure the attribute is populated
* since Spring Session is not aware of the authentication mechanism being used.
* </p>
*
* @since 1.1
*/
String PRINCIPAL_NAME_INDEX_NAME = FindByIndexNameSessionRepository.class.getName().concat(".PRINCIPAL_NAME_INDEX_NAME");
String PRINCIPAL_NAME_INDEX_NAME = FindByIndexNameSessionRepository.class.getName()
.concat(".PRINCIPAL_NAME_INDEX_NAME");
/**
* Find a Map of the session id to the {@link Session} of all sessions that
* contain the session attribute with the name
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and
* the value of the specified principal name.
* Find a Map of the session id to the {@link Session} of all sessions that contain
* the session attribute with the name
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} and the value of
* the specified principal name.
*
* @param indexName
* the name if the index (i.e. {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
* @param indexName the name if the index (i.e.
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
* @param indexValue the value of the index to search for.
* @return a Map (never null) of the session id to the {@link Session} of
* all sessions that contain the session specified index name and
* the value of the specified index name. If no results are found,
* an empty Map is returned.
* @return a Map (never null) of the session id to the {@link Session} of all sessions
* that contain the session specified index name and the value of the specified index
* name. If no results are found, an empty Map is returned.
*/
Map<String, S> findByIndexNameAndIndexValue(String indexName, String indexValue);
}

View File

@@ -25,17 +25,19 @@ import java.util.concurrent.TimeUnit;
/**
* <p>
* A {@link Session} implementation that is backed by a {@link java.util.Map}. The defaults for the properties are:
* A {@link Session} implementation that is backed by a {@link java.util.Map}. The
* defaults for the properties are:
* </p>
* <ul>
* <li>id - a secure random generated id</li>
* <li>creationTime - the moment the {@link MapSession} was instantiated</li>
* <li>lastAccessedTime - the moment the {@link MapSession} was instantiated</li>
* <li>maxInactiveInterval - 30 minutes</li>
* <li>id - a secure random generated id</li>
* <li>creationTime - the moment the {@link MapSession} was instantiated</li>
* <li>lastAccessedTime - the moment the {@link MapSession} was instantiated</li>
* <li>maxInactiveInterval - 30 minutes</li>
* </ul>
*
* <p>
* This implementation has no synchronization, so it is best to use the copy constructor when working on multiple threads.
* This implementation has no synchronization, so it is best to use the copy constructor
* when working on multiple threads.
* </p>
*
* @author Rob Winch
@@ -65,9 +67,9 @@ public final class MapSession implements ExpiringSession, Serializable {
}
/**
* Creates a new instance with the specified id. This is preferred to the
* default constructor when the id is known to prevent unnecessary consumption on
* entropy which can be slow.
* Creates a new instance with the specified id. This is preferred to the default
* constructor when the id is known to prevent unnecessary consumption on entropy
* which can be slow.
*
* @param id the identifier to use
*/
@@ -78,14 +80,16 @@ public final class MapSession implements ExpiringSession, Serializable {
/**
* Creates a new instance from the provided {@link Session}.
*
* @param session the {@link Session} to initialize this {@link Session} with. Cannot be null.
* @param session the {@link Session} to initialize this {@link Session} with. Cannot
* be null.
*/
public MapSession(ExpiringSession session) {
if (session == null) {
throw new IllegalArgumentException("session cannot be null");
}
this.id = session.getId();
this.sessionAttrs = new HashMap<String, Object>(session.getAttributeNames().size());
this.sessionAttrs = new HashMap<String, Object>(
session.getAttributeNames().size());
for (String attrName : session.getAttributeNames()) {
Object attrValue = session.getAttribute(attrName);
this.sessionAttrs.put(attrName, attrValue);
@@ -127,7 +131,8 @@ public final class MapSession implements ExpiringSession, Serializable {
if (this.maxInactiveInterval < 0) {
return false;
}
return now - TimeUnit.SECONDS.toMillis(this.maxInactiveInterval) >= this.lastAccessedTime;
return now - TimeUnit.SECONDS
.toMillis(this.maxInactiveInterval) >= this.lastAccessedTime;
}
@SuppressWarnings("unchecked")
@@ -153,15 +158,19 @@ public final class MapSession implements ExpiringSession, Serializable {
}
/**
* Sets the time that this {@link Session} was created in milliseconds since midnight of 1/1/1970 GMT. The default is when the {@link Session} was instantiated.
* @param creationTime the time that this {@link Session} was created in milliseconds since midnight of 1/1/1970 GMT.
* Sets the time that this {@link Session} was created in milliseconds since midnight
* of 1/1/1970 GMT. The default is when the {@link Session} was instantiated.
* @param creationTime the time that this {@link Session} was created in milliseconds
* since midnight of 1/1/1970 GMT.
*/
public void setCreationTime(long creationTime) {
this.creationTime = creationTime;
}
/**
* Sets the identifier for this {@link Session}. The id should be a secure random generated value to prevent malicious users from guessing this value. The default is a secure random generated identifier.
* Sets the identifier for this {@link Session}. The id should be a secure random
* generated value to prevent malicious users from guessing this value. The default is
* a secure random generated identifier.
*
* @param id the identifier for this session.
*/

View File

@@ -23,12 +23,14 @@ import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
/**
* A {@link SessionRepository} backed by a {@link java.util.Map} and that uses a {@link MapSession}. By default a
* {@link java.util.concurrent.ConcurrentHashMap} is used, but a custom {@link java.util.Map} can be injected to use
* distributed maps provided by NoSQL stores like Redis and Hazelcast.
* A {@link SessionRepository} backed by a {@link java.util.Map} and that uses a
* {@link MapSession}. By default a {@link java.util.concurrent.ConcurrentHashMap} is
* used, but a custom {@link java.util.Map} can be injected to use distributed maps
* provided by NoSQL stores like Redis and Hazelcast.
*
* <p>
* The implementation does NOT support firing {@link SessionDeletedEvent} or {@link SessionExpiredEvent}.
* The implementation does NOT support firing {@link SessionDeletedEvent} or
* {@link SessionExpiredEvent}.
* </p>
*
* @author Rob Winch
@@ -36,7 +38,8 @@ import org.springframework.session.events.SessionExpiredEvent;
*/
public class MapSessionRepository implements SessionRepository<ExpiringSession> {
/**
* If non-null, this value is used to override {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
* If non-null, this value is used to override
* {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
*/
private Integer defaultMaxInactiveInterval;
@@ -50,7 +53,8 @@ public class MapSessionRepository implements SessionRepository<ExpiringSession>
}
/**
* Creates a new instance backed by the provided {@link java.util.Map}. This allows injecting a distributed {@link java.util.Map}.
* Creates a new instance backed by the provided {@link java.util.Map}. This allows
* injecting a distributed {@link java.util.Map}.
*
* @param sessions the {@link java.util.Map} to use. Cannot be null.
*/
@@ -62,8 +66,10 @@ public class MapSessionRepository implements SessionRepository<ExpiringSession>
}
/**
* If non-null, this value is used to override {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between client requests.
* If non-null, this value is used to override
* {@link ExpiringSession#setMaxInactiveIntervalInSeconds(int)}.
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
* should be kept alive between client requests.
*/
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);

View File

@@ -19,8 +19,8 @@ package org.springframework.session;
import java.util.Set;
/**
* Provides a way to identify a user in an agnostic way. This allows the session to be used by an HttpSession, WebSocket
* Session, or even non web related sessions.
* Provides a way to identify a user in an agnostic way. This allows the session to be
* used by an HttpSession, WebSocket Session, or even non web related sessions.
*
* @author Rob Winch
* @since 1.0
@@ -35,16 +35,20 @@ public interface Session {
String getId();
/**
* Gets the Object associated with the specified name or null if no Object is associated to that name.
* Gets the Object associated with the specified name or null if no Object is
* associated to that name.
*
* @param attributeName the name of the attribute to get
* @return the Object associated with the specified name or null if no Object is associated to that name
* @return the Object associated with the specified name or null if no Object is
* associated to that name
* @param <T> The return type of the attribute
*/
<T> T getAttribute(String attributeName);
/**
* Gets the attribute names that have a value associated with it. Each value can be passed into {@link org.springframework.session.Session#getAttribute(String)} to obtain the attribute value.
* Gets the attribute names that have a value associated with it. Each value can be
* passed into {@link org.springframework.session.Session#getAttribute(String)} to
* obtain the attribute value.
*
* @return the attribute names that have a value associated with it.
* @see #getAttribute(String)
@@ -52,10 +56,13 @@ public interface Session {
Set<String> getAttributeNames();
/**
* Sets the attribute value for the provided attribute name. If the attributeValue is null, it has the same result as removing the attribute with {@link org.springframework.session.Session#removeAttribute(String)} .
* Sets the attribute value for the provided attribute name. If the attributeValue is
* null, it has the same result as removing the attribute with
* {@link org.springframework.session.Session#removeAttribute(String)} .
*
* @param attributeName the attribute name to set
* @param attributeValue the value of the attribute to set. If null, the attribute will be removed.
* @param attributeValue the value of the attribute to set. If null, the attribute
* will be removed.
*/
void setAttribute(String attributeName, Object attributeValue);

View File

@@ -26,22 +26,28 @@ package org.springframework.session;
public interface SessionRepository<S extends Session> {
/**
* Creates a new {@link Session} that is capable of being persisted by this {@link SessionRepository}.
* Creates a new {@link Session} that is capable of being persisted by this
* {@link SessionRepository}.
*
* <p>This allows optimizations and customizations in how the {@link Session} is persisted. For example, the
* implementation returned might keep track of the changes ensuring that only the delta needs to be persisted on
* a save.</p>
* <p>
* This allows optimizations and customizations in how the {@link Session} is
* persisted. For example, the implementation returned might keep track of the changes
* ensuring that only the delta needs to be persisted on a save.
* </p>
*
* @return a new {@link Session} that is capable of being persisted by this {@link SessionRepository}
* @return a new {@link Session} that is capable of being persisted by this
* {@link SessionRepository}
*/
S createSession();
/**
* Ensures the {@link Session} created by {@link org.springframework.session.SessionRepository#createSession()} is saved.
* Ensures the {@link Session} created by
* {@link org.springframework.session.SessionRepository#createSession()} is saved.
*
* <p>
* Some implementations may choose to save as the {@link Session} is updated by returning a {@link Session} that
* immediately persists any changes. In this case, this method may not actually do anything.
* Some implementations may choose to save as the {@link Session} is updated by
* returning a {@link Session} that immediately persists any changes. In this case,
* this method may not actually do anything.
* </p>
*
* @param session the {@link Session} to save
@@ -49,15 +55,18 @@ public interface SessionRepository<S extends Session> {
void save(S session);
/**
* Gets the {@link Session} by the {@link Session#getId()} or null if no {@link Session} is found.
* Gets the {@link Session} by the {@link Session#getId()} or null if no
* {@link Session} is found.
*
* @param id the {@link org.springframework.session.Session#getId()} to lookup
* @return the {@link Session} by the {@link Session#getId()} or null if no {@link Session} is found.
* @return the {@link Session} by the {@link Session#getId()} or null if no
* {@link Session} is found.
*/
S getSession(String id);
/**
* Deletes the {@link Session} with the given {@link Session#getId()} or does nothing if the {@link Session} is not found.
* Deletes the {@link Session} with the given {@link Session#getId()} or does nothing
* if the {@link Session} is not found.
* @param id the {@link org.springframework.session.Session#getId()} to delete
*/
void delete(String id);

View File

@@ -28,10 +28,9 @@ 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:
* 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>
@@ -45,15 +44,13 @@ import org.springframework.session.events.SessionDestroyedEvent;
* }
*
* }
* </code>
* </pre>
* </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.
* 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>
@@ -61,13 +58,12 @@ import org.springframework.session.events.SessionDestroyedEvent;
* </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>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>
*

View File

@@ -37,8 +37,8 @@ import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapt
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:
* 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}
@@ -54,11 +54,10 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* </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.
* 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>
@@ -66,13 +65,12 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* </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>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>
*
@@ -98,11 +96,14 @@ public class SpringHttpSessionConfiguration {
}
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(sessionRepository);
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
sessionRepository);
sessionRepositoryFilter.setServletContext(this.servletContext);
if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
sessionRepositoryFilter.setHttpSessionStrategy((MultiHttpSessionStrategy) this.httpSessionStrategy);
sessionRepositoryFilter.setHttpSessionStrategy(
(MultiHttpSessionStrategy) this.httpSessionStrategy);
}
else {
sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);

View File

@@ -64,8 +64,9 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* AbstractGemFireOperationsSessionRepository is an abstract base class encapsulating functionality common
* to all implementations that support SessionRepository operations backed by GemFire.
* AbstractGemFireOperationsSessionRepository is an abstract base class encapsulating
* functionality common to all implementations that support SessionRepository operations
* backed by GemFire.
*
* @author John Blum
* @since 1.1.0
@@ -77,19 +78,22 @@ import org.springframework.util.StringUtils;
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.FindByIndexNameSessionRepository
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* GemFireHttpSessionConfiguration
* @see com.gemstone.gemfire.cache.Region
* @see com.gemstone.gemfire.cache.util.CacheListenerAdapter
*/
public abstract class AbstractGemFireOperationsSessionRepository extends CacheListenerAdapter<Object, ExpiringSession>
public abstract class AbstractGemFireOperationsSessionRepository
extends CacheListenerAdapter<Object, ExpiringSession>
implements InitializingBean, FindByIndexNameSessionRepository<ExpiringSession>,
ApplicationEventPublisherAware {
ApplicationEventPublisherAware {
private int maxInactiveIntervalInSeconds = GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS;
private ApplicationEventPublisher applicationEventPublisher = new ApplicationEventPublisher() {
public void publishEvent(ApplicationEvent event) {
}
public void publishEvent(Object event) {
}
};
@@ -101,8 +105,9 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
private String fullyQualifiedRegionName;
/**
* Constructs an instance of AbstractGemFireOperationsSessionRepository with a required GemfireOperations instance
* used to perform GemFire data access operations and interactions supporting the SessionRepository operations.
* Constructs an instance of AbstractGemFireOperationsSessionRepository with a
* required GemfireOperations instance used to perform GemFire data access operations
* and interactions supporting the SessionRepository operations.
*
* @param template the GemfireOperations instance used to interact with GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
@@ -123,18 +128,23 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* Sets the ApplicationEventPublisher used to publish Session events corresponding to GemFire cache events.
* Sets the ApplicationEventPublisher used to publish Session events corresponding to
* GemFire cache events.
*
* @param applicationEventPublisher the Spring ApplicationEventPublisher used to publish Session-based events.
* @param applicationEventPublisher the Spring ApplicationEventPublisher used to
* publish Session-based events.
* @see org.springframework.context.ApplicationEventPublisher
*/
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher, "ApplicationEventPublisher must not be null");
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher,
"ApplicationEventPublisher must not be null");
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* Gets the ApplicationEventPublisher used to publish Session events corresponding to GemFire cache events.
* Gets the ApplicationEventPublisher used to publish Session events corresponding to
* GemFire cache events.
*
* @return the Spring ApplicationEventPublisher used to publish Session-based events.
* @see org.springframework.context.ApplicationEventPublisher
@@ -144,38 +154,43 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* Gets the fully-qualified name of the GemFire cache {@link Region} used to store and manage Session data.
* Gets the fully-qualified name of the GemFire cache {@link Region} used to store and
* manage Session data.
*
* @return a String indicating the fully qualified name of the GemFire cache {@link Region} used to store
* and manage Session data.
* @return a String indicating the fully qualified name of the GemFire cache
* {@link Region} used to store and manage Session data.
*/
protected String getFullyQualifiedRegionName() {
return this.fullyQualifiedRegionName;
}
/**
* Sets the maximum interval in seconds in which a Session can remain inactive before it is considered expired.
* Sets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum
* interval in seconds that a Session can remain inactive before it is considered
* expired.
*/
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
/**
* Gets the maximum interval in seconds in which a Session can remain inactive before it is considered expired.
* Gets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @return an integer value specifying the maximum interval in seconds that a Session can remain inactive
* before it is considered expired.
* @return an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
*/
public int getMaxInactiveIntervalInSeconds() {
return this.maxInactiveIntervalInSeconds;
}
/**
* Gets a reference to the GemfireOperations (template) used to perform data access operations
* and other interactions on the GemFire cache {@link Region} backing this SessionRepository.
* Gets a reference to the GemfireOperations (template) used to perform data access
* operations and other interactions on the GemFire cache {@link Region} backing this
* SessionRepository.
*
* @return a reference to the GemfireOperations used to interact with GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
@@ -185,9 +200,10 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* Callback method during Spring bean initialization that will capture the fully-qualified name
* of the GemFire cache {@link Region} used to manage Session state and register this SessionRepository
* as a GemFire {@link com.gemstone.gemfire.cache.CacheListener}.
* Callback method during Spring bean initialization that will capture the
* fully-qualified name of the GemFire cache {@link Region} used to manage Session
* state and register this SessionRepository as a GemFire
* {@link com.gemstone.gemfire.cache.CacheListener}.
*
* @throws Exception if an error occurs during the initialization process.
*/
@@ -213,7 +229,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* Callback method triggered when an entry is created in the GemFire cache {@link Region}.
* Callback method triggered when an entry is created in the GemFire cache
* {@link Region}.
*
* @param event an EntryEvent containing the details of the cache operation.
* @see com.gemstone.gemfire.cache.EntryEvent
@@ -222,12 +239,14 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
@Override
public void afterCreate(EntryEvent<Object, ExpiringSession> event) {
if (isExpiringSessionOrNull(event.getNewValue())) {
handleCreated(event.getKey().toString(), toExpiringSession(event.getNewValue()));
handleCreated(event.getKey().toString(),
toExpiringSession(event.getNewValue()));
}
}
/**
* Callback method triggered when an entry is destroyed in the GemFire cache {@link Region}.
* Callback method triggered when an entry is destroyed in the GemFire cache
* {@link Region}.
*
* @param event an EntryEvent containing the details of the cache operation.
* @see com.gemstone.gemfire.cache.EntryEvent
@@ -235,11 +254,13 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
*/
@Override
public void afterDestroy(EntryEvent<Object, ExpiringSession> event) {
handleDestroyed(event.getKey().toString(), toExpiringSession(event.getOldValue()));
handleDestroyed(event.getKey().toString(),
toExpiringSession(event.getOldValue()));
}
/**
* Callback method triggered when an entry is invalidated in the GemFire cache {@link Region}.
* Callback method triggered when an entry is invalidated in the GemFire cache
* {@link Region}.
*
* @param event an EntryEvent containing the details of the cache operation.
* @see com.gemstone.gemfire.cache.EntryEvent
@@ -261,7 +282,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
*/
protected void handleCreated(String sessionId, ExpiringSession session) {
publishEvent(session != null ? new SessionCreatedEvent(this, session)
: new SessionCreatedEvent(this, sessionId));
: new SessionCreatedEvent(this, sessionId));
}
/**
@@ -275,7 +296,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
*/
protected void handleDeleted(String sessionId, ExpiringSession session) {
publishEvent(session != null ? new SessionDeletedEvent(this, session)
: new SessionDeletedEvent(this, sessionId));
: new SessionDeletedEvent(this, sessionId));
}
/**
@@ -289,7 +310,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
*/
protected void handleDestroyed(String sessionId, ExpiringSession session) {
publishEvent(session != null ? new SessionDestroyedEvent(this, session)
: new SessionDestroyedEvent(this, sessionId));
: new SessionDestroyedEvent(this, sessionId));
}
/**
@@ -303,7 +324,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
*/
protected void handleExpired(String sessionId, ExpiringSession session) {
publishEvent(session != null ? new SessionExpiredEvent(this, session)
: new SessionExpiredEvent(this, sessionId));
: new SessionExpiredEvent(this, sessionId));
}
/**
@@ -318,29 +339,34 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
getApplicationEventPublisher().publishEvent(event);
}
catch (Throwable t) {
this.logger.error(String.format("error occurred publishing event (%1$s)", event), t);
this.logger.error(
String.format("error occurred publishing event (%1$s)", event), t);
}
}
/**
* GemFireSession is a GemFire representation model of a Spring {@link ExpiringSession} for storing and accessing
* Session state information in GemFire. This class implements GemFire's {@link DataSerializable} interface
* to better handle replication of Session information across the GemFire cluster.
* GemFireSession is a GemFire representation model of a Spring
* {@link ExpiringSession} for storing and accessing Session state information in
* GemFire. This class implements GemFire's {@link DataSerializable} interface to
* better handle replication of Session information across the GemFire cluster.
*
* @see java.lang.Comparable
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes
* @see org.springframework.session.data.gemfire.
* AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes
* @see com.gemstone.gemfire.DataSerializable
* @see com.gemstone.gemfire.DataSerializer
* @see com.gemstone.gemfire.Delta
* @see com.gemstone.gemfire.Instantiator
*/
@SuppressWarnings("serial")
public static class GemFireSession implements Comparable<ExpiringSession>, DataSerializable, Delta, ExpiringSession {
public static class GemFireSession implements Comparable<ExpiringSession>,
DataSerializable, Delta, ExpiringSession {
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
protected static final DateFormat TO_STRING_DATE_FORMAT = new SimpleDateFormat("YYYY-MM-dd-HH-mm-ss");
protected static final DateFormat TO_STRING_DATE_FORMAT = new SimpleDateFormat(
"YYYY-MM-dd-HH-mm-ss");
protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@@ -360,7 +386,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
private long creationTime;
private long lastAccessedTime;
private transient final GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(this);
private transient final GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(
this);
private transient final SpelExpressionParser parser = new SpelExpressionParser();
@@ -455,12 +482,13 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
long maxInactiveIntervalInSeconds = getMaxInactiveIntervalInSeconds();
return (maxInactiveIntervalInSeconds >= 0
&& (idleTimeout(maxInactiveIntervalInSeconds) >= lastAccessedTime));
&& (idleTimeout(maxInactiveIntervalInSeconds) >= lastAccessedTime));
}
/* (non-Javadoc) */
private long idleTimeout(long maxInactiveIntervalInSeconds) {
return (System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(maxInactiveIntervalInSeconds));
return (System.currentTimeMillis()
- TimeUnit.SECONDS.toMillis(maxInactiveIntervalInSeconds));
}
/* (non-Javadoc) */
@@ -475,7 +503,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/* (non-Javadoc) */
public synchronized void setMaxInactiveIntervalInSeconds(final int maxInactiveIntervalInSeconds) {
public synchronized void setMaxInactiveIntervalInSeconds(
final int maxInactiveIntervalInSeconds) {
this.delta |= (this.maxInactiveIntervalInSeconds != maxInactiveIntervalInSeconds);
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
@@ -498,7 +527,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
Object authentication = getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = this.parser.parseExpression("authentication?.name");
Expression expression = this.parser
.parseExpression("authentication?.name");
principalName = expression.getValue(authentication, String.class);
}
}
@@ -514,7 +544,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
out.writeInt(getMaxInactiveIntervalInSeconds());
String principalName = getPrincipalName();
int length = (StringUtils.hasText(principalName) ? principalName.length() : 0);
int length = (StringUtils.hasText(principalName) ? principalName.length()
: 0);
out.writeInt(length);
@@ -533,7 +564,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/* (non-Javadoc) */
public synchronized void fromData(DataInput in) throws ClassNotFoundException, IOException {
public synchronized void fromData(DataInput in)
throws ClassNotFoundException, IOException {
this.id = in.readUTF();
this.creationTime = in.readLong();
setLastAccessedTime(in.readLong());
@@ -609,10 +641,12 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
/* (non-Javadoc) */
@Override
public synchronized String toString() {
return String.format("{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s"
+ ", maxInactiveIntervalInSeconds = %5$s, principalName = %6$s }", getClass().getName(), getId(),
toString(getCreationTime()), toString(getLastAccessedTime()), getMaxInactiveIntervalInSeconds(),
getPrincipalName());
return String.format(
"{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s"
+ ", maxInactiveIntervalInSeconds = %5$s, principalName = %6$s }",
getClass().getName(), getId(), toString(getCreationTime()),
toString(getLastAccessedTime()), getMaxInactiveIntervalInSeconds(),
getPrincipalName());
}
/* (non-Javadoc) */
@@ -622,10 +656,11 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
}
/**
* The GemFireSessionAttributes class is a container for Session attributes that implements both
* the {@link DataSerializable} and {@link Delta} GemFire interfaces for efficient storage and distribution
* (replication) in GemFire. Additionally, GemFireSessionAttributes extends {@link AbstractMap} providing
* {@link Map}-like behavior since attributes of a Session are effectively a name to value mapping.
* The GemFireSessionAttributes class is a container for Session attributes that
* implements both the {@link DataSerializable} and {@link Delta} GemFire interfaces
* for efficient storage and distribution (replication) in GemFire. Additionally,
* GemFireSessionAttributes extends {@link AbstractMap} providing {@link Map}-like
* behavior since attributes of a Session are effectively a name to value mapping.
*
* @see java.util.AbstractMap
* @see com.gemstone.gemfire.DataSerializable
@@ -640,12 +675,13 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
protected static final boolean DEFAULT_ALLOW_JAVA_SERIALIZATION = true;
static {
Instantiator.register(new Instantiator(GemFireSessionAttributes.class, 800828008) {
@Override
public DataSerializable newInstance() {
return new GemFireSessionAttributes();
}
});
Instantiator.register(
new Instantiator(GemFireSessionAttributes.class, 800828008) {
@Override
public DataSerializable newInstance() {
return new GemFireSessionAttributes();
}
});
}
private transient final Map<String, Object> sessionAttributes = new HashMap<String, Object>();
@@ -667,7 +703,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
public void setAttribute(String attributeName, Object attributeValue) {
synchronized (this.lock) {
if (attributeValue != null) {
if (!attributeValue.equals(this.sessionAttributes.put(attributeName, attributeValue))) {
if (!attributeValue.equals(
this.sessionAttributes.put(attributeName, attributeValue))) {
this.sessionAttributeDeltas.put(attributeName, attributeValue);
}
}
@@ -697,7 +734,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
/* (non-Javadoc) */
public Set<String> getAttributeNames() {
synchronized (this.lock) {
return Collections.unmodifiableSet(new HashSet<String>(this.sessionAttributes.keySet()));
return Collections.unmodifiableSet(
new HashSet<String>(this.sessionAttributes.keySet()));
}
}
@@ -713,7 +751,10 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
return new AbstractSet<Entry<String, Object>>() {
@Override
public Iterator<Entry<String, Object>> iterator() {
return Collections.unmodifiableMap(GemFireSessionAttributes.this.sessionAttributes).entrySet().iterator();
return Collections
.unmodifiableMap(
GemFireSessionAttributes.this.sessionAttributes)
.entrySet().iterator();
}
@Override
@@ -736,7 +777,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
public void from(GemFireSessionAttributes sessionAttributes) {
synchronized (this.lock) {
for (String attributeName : sessionAttributes.getAttributeNames()) {
setAttribute(attributeName, sessionAttributes.getAttribute(attributeName));
setAttribute(attributeName,
sessionAttributes.getAttribute(attributeName));
}
}
}
@@ -788,7 +830,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi
synchronized (this.lock) {
out.writeInt(this.sessionAttributeDeltas.size());
for (Map.Entry<String, Object> entry : this.sessionAttributeDeltas.entrySet()) {
for (Map.Entry<String, Object> entry : this.sessionAttributeDeltas
.entrySet()) {
out.writeUTF(entry.getKey());
writeObject(entry.getValue(), out);
}

View File

@@ -25,31 +25,33 @@ import org.springframework.data.gemfire.GemfireOperations;
import org.springframework.session.ExpiringSession;
/**
* The GemFireOperationsSessionRepository class is a Spring SessionRepository implementation that interfaces with
* and uses GemFire to back and store Spring Sessions.
* The GemFireOperationsSessionRepository class is a Spring SessionRepository
* implementation that interfaces with and uses GemFire to back and store Spring Sessions.
*
* @author John Blum
* @since 1.1.0
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.Session
* @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.
* AbstractGemFireOperationsSessionRepository
*/
public class GemFireOperationsSessionRepository extends AbstractGemFireOperationsSessionRepository {
public class GemFireOperationsSessionRepository
extends AbstractGemFireOperationsSessionRepository {
// GemFire OQL query used to lookup Sessions by arbitrary attributes.
protected static final String FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY =
"SELECT s FROM %1$s s WHERE s.attributes['%2$s'] = $1";
protected static final String FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY = "SELECT s FROM %1$s s WHERE s.attributes['%2$s'] = $1";
// GemFire OQL query used to look up Sessions by principal name.
protected static final String FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY =
"SELECT s FROM %1$s s WHERE s.principalName = $1";
protected static final String FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY = "SELECT s FROM %1$s s WHERE s.principalName = $1";
/**
* Constructs an instance of GemFireOperationsSessionRepository initialized with the required GemfireOperations
* object used to perform data access operations to manage Session state.
* Constructs an instance of GemFireOperationsSessionRepository initialized with the
* required GemfireOperations object used to perform data access operations to manage
* Session state.
*
* @param template the GemfireOperations object used to access and manage Session state in GemFire.
* @param template the GemfireOperations object used to access and manage Session
* state in GemFire.
* @see org.springframework.data.gemfire.GemfireOperations
*/
public GemFireOperationsSessionRepository(GemfireOperations template) {
@@ -57,20 +59,26 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
}
/**
* Looks up all available Sessions with the particular attribute indexed by name having the given value.
* Looks up all available Sessions with the particular attribute indexed by name
* having the given value.
*
* @param indexName name of the indexed Session attribute.
* (e.g. {@link org.springframework.session.FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}).
* @param indexValue value of the indexed Session attribute to search on (e.g. username).
* @param indexName name of the indexed Session attribute. (e.g.
* {@link org.springframework.session.FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME}
* ).
* @param indexValue value of the indexed Session attribute to search on (e.g.
* username).
* @return a mapping of Session ID to Session instances.
* @see org.springframework.session.ExpiringSession
* @see java.util.Map
* @see #prepareQuery(String)
*/
public Map<String, ExpiringSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
SelectResults<ExpiringSession> results = getTemplate().find(prepareQuery(indexName), indexValue);
public Map<String, ExpiringSession> findByIndexNameAndIndexValue(String indexName,
String indexValue) {
SelectResults<ExpiringSession> results = getTemplate()
.find(prepareQuery(indexName), indexValue);
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(results.size());
Map<String, ExpiringSession> sessions = new HashMap<String, ExpiringSession>(
results.size());
for (ExpiringSession session : results.asList()) {
sessions.put(session.getId(), session);
@@ -80,22 +88,26 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
}
/**
* Prepares the appropriate GemFire OQL query based on the indexed Session attribute name.
* Prepares the appropriate GemFire OQL query based on the indexed Session attribute
* name.
*
* @param indexName a String indicating the name of the indexed Session attribute.
* @return an appropriate GemFire OQL statement for querying on a particular indexed Session attribute.
* @return an appropriate GemFire OQL statement for querying on a particular indexed
* Session attribute.
*/
protected String prepareQuery(String indexName) {
return (PRINCIPAL_NAME_INDEX_NAME.equals(indexName)
? String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, getFullyQualifiedRegionName())
: String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY, getFullyQualifiedRegionName(), indexName));
? String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
getFullyQualifiedRegionName())
: String.format(FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
getFullyQualifiedRegionName(), indexName));
}
/**
* Constructs a new {@link ExpiringSession} instance backed by GemFire.
*
* @return an instance of {@link ExpiringSession} backed by GemFire.
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository.GemFireSession#create(int)
* @see GemFireSession#create(int)
* @see org.springframework.session.ExpiringSession
* @see #getMaxInactiveIntervalInSeconds()
*/
@@ -104,8 +116,8 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
}
/**
* Gets a copy of an existing, non-expired {@link ExpiringSession} by ID. If the Session is expired,
* then it is deleted.
* Gets a copy of an existing, non-expired {@link ExpiringSession} by ID. If the
* Session is expired, then it is deleted.
*
* @param sessionId a String indicating the ID of the Session to get.
* @return an existing {@link ExpiringSession} by ID or null if not Session exists.
@@ -140,15 +152,16 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation
}
/**
* Deletes (removes) any existing {@link ExpiringSession} from GemFire. This operation also results in
* a SessionDeletedEvent.
* Deletes (removes) any existing {@link ExpiringSession} from GemFire. This operation
* also results in a SessionDeletedEvent.
*
* @param sessionId a String indicating the ID of the Session to remove from GemFire.
* @see org.springframework.data.gemfire.GemfireOperations#remove(Object)
* @see #handleDeleted(String, ExpiringSession)
*/
public void delete(String sessionId) {
handleDeleted(sessionId, getTemplate().<Object, ExpiringSession>remove(sessionId));
handleDeleted(sessionId,
getTemplate().<Object, ExpiringSession>remove(sessionId));
}
}

View File

@@ -29,11 +29,13 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Add this annotation to an {@code @Configuration} class to expose the SessionRepositoryFilter
* as a bean named "springSessionRepositoryFilter" and backed by Pivotal GemFire or Apache Geode.
* Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
* Pivotal GemFire or Apache Geode.
*
* In order to leverage the annotation, a single Pivotal GemFire/Apache Geode {@link com.gemstone.gemfire.cache.Cache}
* or {@link com.gemstone.gemfire.cache.client.ClientCache} instance must be provided.
* In order to leverage the annotation, a single Pivotal GemFire/Apache Geode
* {@link com.gemstone.gemfire.cache.Cache} or
* {@link com.gemstone.gemfire.cache.client.ClientCache} instance must be provided.
*
* For example:
*
@@ -61,12 +63,11 @@ import org.springframework.context.annotation.Import;
* return clientCacheFactoryBean;
* }
* }
* </code>
* </pre>
* </code> </pre>
*
* Alternatively, a Spring Session can be configured to use Pivotal GemFire (Apache Geode) as a client
* using a dedicated GemFire Server cluster and a {@link com.gemstone.gemfire.cache.client.ClientCache}.
* For example:
* Alternatively, a Spring Session can be configured to use Pivotal GemFire (Apache Geode)
* as a client using a dedicated GemFire Server cluster and a
* {@link com.gemstone.gemfire.cache.client.ClientCache}. For example:
*
* <code>
* {@literal @Configuration}
@@ -92,12 +93,14 @@ import org.springframework.context.annotation.Import;
* }
* </code>
*
* More advanced configurations can extend {@link GemFireHttpSessionConfiguration} instead.
* More advanced configurations can extend {@link GemFireHttpSessionConfiguration}
* instead.
*
* @author John Blum
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* GemFireHttpSessionConfiguration
* @see org.springframework.session.config.annotation.web.http.EnableSpringHttpSession
* @since 1.1.0
*/
@@ -111,31 +114,35 @@ public @interface EnableGemFireHttpSession {
/**
* Defines the GemFire ClientCache Region DataPolicy.
*
* @return a ClientRegionShortcut used to specify and configure the ClientCache Region DataPolicy.
* @return a ClientRegionShortcut used to specify and configure the ClientCache Region
* DataPolicy.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
ClientRegionShortcut clientRegionShortcut() default ClientRegionShortcut.PROXY;
/**
* Identifies the Session attributes by name that should be indexed for query operations.
* For instance, find all Sessions in GemFire having attribute A defined with value X.
* Identifies the Session attributes by name that should be indexed for query
* operations. For instance, find all Sessions in GemFire having attribute A defined
* with value X.
*
* @return an array of Strings identifying the names of Session attributes to index.
*/
String[] indexableSessionAttributes() default {};
String[]indexableSessionAttributes() default {};
/**
* Defines the maximum interval in seconds that a Session can remain inactive before it is considered expired.
* Defaults to 1800 seconds, or 30 minutes.
* Defines the maximum interval in seconds that a Session can remain inactive before
* it is considered expired. Defaults to 1800 seconds, or 30 minutes.
*
* @return an integer value defining the maximum inactive interval in seconds for declaring a Session expired.
* @return an integer value defining the maximum inactive interval in seconds for
* declaring a Session expired.
*/
int maxInactiveIntervalInSeconds() default 1800;
/**
* Defines the name of the GemFire (Client)Cache Region used to store Sessions.
*
* @return a String specifying the name of the GemFire (Client)Cache Region used to store Sessions.
* @return a String specifying the name of the GemFire (Client)Cache Region used to
* store Sessions.
* @see com.gemstone.gemfire.cache.Region#getName()
*/
String regionName() default "ClusteredSpringSessions";
@@ -143,7 +150,8 @@ public @interface EnableGemFireHttpSession {
/**
* Defines the GemFire, Peer Cache Region DataPolicy.
*
* @return a RegionShortcut used to specify and configure the Peer Cache Region DataPolicy.
* @return a RegionShortcut used to specify and configure the Peer Cache Region
* DataPolicy.
* @see com.gemstone.gemfire.cache.RegionShortcut
*/
RegionShortcut serverRegionShortcut() default RegionShortcut.PARTITION;

View File

@@ -49,8 +49,9 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* The GemFireHttpSessionConfiguration class is a Spring @Configuration class used to configure and initialize
* Pivotal GemFire (or Apache Geode) as a clustered, replicated HttpSession provider implementation in Spring Session.
* The GemFireHttpSessionConfiguration class is a Spring @Configuration class used to
* configure and initialize Pivotal GemFire (or Apache Geode) as a clustered, replicated
* HttpSession provider implementation in Spring Session.
*
* @author John Blum
* @since 1.1.0
@@ -63,9 +64,11 @@ import org.springframework.util.StringUtils;
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see org.springframework.data.gemfire.RegionAttributesFactoryBean
* @see org.springframework.session.ExpiringSession
* @see org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration
* @see org.springframework.session.config.annotation.web.http.
* SpringHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.
* GemFireCacheTypeAwareRegionFactoryBean
* @see com.gemstone.gemfire.cache.ExpirationAttributes
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
@@ -81,7 +84,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;
@@ -121,9 +125,11 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
private String[] indexableSessionAttributes = DEFAULT_INDEXABLE_SESSION_ATTRIBUTES;
/**
* Sets a reference to the {@link ClassLoader} used to load bean definition class types in a Spring context.
* Sets a reference to the {@link ClassLoader} used to load bean definition class
* types in a Spring context.
*
* @param beanClassLoader the ClassLoader used by the Spring container to load bean class types.
* @param beanClassLoader the ClassLoader used by the Spring container to load bean
* class types.
* @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(ClassLoader)
* @see java.lang.ClassLoader
*/
@@ -132,7 +138,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Gets a reference to the {@link ClassLoader} used to load bean definition class types in a Spring context.
* Gets a reference to the {@link ClassLoader} used to load bean definition class
* types in a Spring context.
*
* @return the ClassLoader used by the Spring container to load bean class types.
* @see java.lang.ClassLoader
@@ -142,10 +149,11 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Sets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache Region
* that will store Spring Sessions.
* Sets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache
* Region that will store Spring Sessions.
*
* @param shortcut the ClientRegionShortcut used to configure the GemFire ClientCache Region.
* @param shortcut the ClientRegionShortcut used to configure the GemFire ClientCache
* Region.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
public void setClientRegionShortcut(ClientRegionShortcut shortcut) {
@@ -153,22 +161,24 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Gets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache Region
* that will store Spring Sessions. Defaults to {@link ClientRegionShortcut#PROXY}.
* Gets the {@link ClientRegionShortcut} used to configure the GemFire ClientCache
* Region that will store Spring Sessions. Defaults to
* {@link ClientRegionShortcut#PROXY}.
*
* @return the ClientRegionShortcut used to configure the GemFire ClientCache Region.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
* @see EnableGemFireHttpSession#clientRegionShortcut()
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (this.clientRegionShortcut != null ? this.clientRegionShortcut : DEFAULT_CLIENT_REGION_SHORTCUT);
return (this.clientRegionShortcut != null ? this.clientRegionShortcut
: DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Sets the names of all Session attributes that should be indexed by GemFire.
*
* @param indexableSessionAttributes an array of Strings indicating the names of all Session attributes
* for which an Index will be created by GemFire.
* @param indexableSessionAttributes an array of Strings indicating the names of all
* Session attributes for which an Index will be created by GemFire.
*/
public void setIndexableSessionAttributes(String[] indexableSessionAttributes) {
this.indexableSessionAttributes = indexableSessionAttributes;
@@ -177,21 +187,23 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
/**
* Get the names of all Session attributes that should be indexed by GemFire.
*
* @return an array of Strings indicating the names of all Session attributes for which an Index
* will be created by GemFire. Defaults to an empty String array if unspecified.
* @return an array of Strings indicating the names of all Session attributes for
* which an Index will be created by GemFire. Defaults to an empty String array if
* unspecified.
* @see EnableGemFireHttpSession#indexableSessionAttributes()
*/
protected String[] getIndexableSessionAttributes() {
return (this.indexableSessionAttributes != null ? this.indexableSessionAttributes : DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
return (this.indexableSessionAttributes != null ? this.indexableSessionAttributes
: DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
}
/**
* Gets the names of all Session attributes that will be indexed by GemFire as single String value constituting
* the Index expression of the Index definition.
* Gets the names of all Session attributes that will be indexed by GemFire as single
* String value constituting the Index expression of the Index definition.
*
* @return a String composed of all the named Session attributes on which a GemFire Index will be created
* as an Index definition expression. If the indexable Session attributes were not specified, then the
* wildcard ("*") is returned.
* @return a String composed of all the named Session attributes on which a GemFire
* Index will be created as an Index definition expression. If the indexable Session
* attributes were not specified, then the wildcard ("*") is returned.
* @see com.gemstone.gemfire.cache.query.Index#getIndexedExpression()
*/
protected String getIndexableSessionAttributesAsGemFireIndexExpression() {
@@ -208,20 +220,23 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Sets the maximum interval in seconds in which a Session can remain inactive before it is considered expired.
* Sets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
* @param maxInactiveIntervalInSeconds an integer value specifying the maximum
* interval in seconds that a Session can remain inactive before it is considered
* expired.
*/
public void setMaxInactiveIntervalInSeconds(int maxInactiveIntervalInSeconds) {
this.maxInactiveIntervalInSeconds = maxInactiveIntervalInSeconds;
}
/**
* Gets the maximum interval in seconds in which a Session can remain inactive before it is considered expired.
* Gets the maximum interval in seconds in which a Session can remain inactive before
* it is considered expired.
*
* @return an integer value specifying the maximum interval in seconds that a Session can remain inactive
* before it is considered expired.
* @return an integer value specifying the maximum interval in seconds that a Session
* can remain inactive before it is considered expired.
* @see EnableGemFireHttpSession#maxInactiveIntervalInSeconds()
*/
protected int getMaxInactiveIntervalInSeconds() {
@@ -229,7 +244,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Sets the {@link RegionShortcut} used to configure the GemFire Cache Region that will store Spring Sessions.
* Sets the {@link RegionShortcut} used to configure the GemFire Cache Region that
* will store Spring Sessions.
*
* @param shortcut the RegionShortcut used to configure the GemFire Cache Region.
* @see com.gemstone.gemfire.cache.RegionShortcut
@@ -239,111 +255,131 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Gets the {@link RegionShortcut} used to configure the GemFire Cache Region that will store Spring Sessions.
* Defaults to {@link RegionShortcut#PARTITION}.
* Gets the {@link RegionShortcut} used to configure the GemFire Cache Region that
* will store Spring Sessions. Defaults to {@link RegionShortcut#PARTITION}.
*
* @return the RegionShortcut used to configure the GemFire Cache Region.
* @see com.gemstone.gemfire.cache.RegionShortcut
* @see EnableGemFireHttpSession#serverRegionShortcut()
*/
protected RegionShortcut getServerRegionShortcut() {
return (this.serverRegionShortcut != null ? this.serverRegionShortcut : DEFAULT_SERVER_REGION_SHORTCUT);
return (this.serverRegionShortcut != null ? this.serverRegionShortcut
: DEFAULT_SERVER_REGION_SHORTCUT);
}
/**
* Sets the name of the Gemfire (Client)Cache Region used to store Sessions.
*
* @param springSessionGemFireRegionName a String specifying the name of the GemFire (Client)Cache Region
* used to store the Session.
* @param springSessionGemFireRegionName a String specifying the name of the GemFire
* (Client)Cache Region used to store the Session.
*/
public void setSpringSessionGemFireRegionName(String springSessionGemFireRegionName) {
this.springSessionGemFireRegionName = springSessionGemFireRegionName;
}
/**
* Gets the name of the Gemfire (Client)Cache Region used to store Sessions. Defaults to 'ClusteredSpringSessions'.
* Gets the name of the Gemfire (Client)Cache Region used to store Sessions. Defaults
* to 'ClusteredSpringSessions'.
*
* @return a String specifying the name of the GemFire (Client)Cache Region
* used to store the Session.
* @return a String specifying the name of the GemFire (Client)Cache Region used to
* store the Session.
* @see com.gemstone.gemfire.cache.Region#getName()
* @see EnableGemFireHttpSession#regionName()
*/
protected String getSpringSessionGemFireRegionName() {
return (StringUtils.hasText(this.springSessionGemFireRegionName) ? this.springSessionGemFireRegionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
return (StringUtils.hasText(this.springSessionGemFireRegionName)
? this.springSessionGemFireRegionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
/**
* Callback with the {@link AnnotationMetadata} of the class containing @Import annotation that imported
* this @Configuration class.
* Callback with the {@link AnnotationMetadata} of the class containing @Import
* annotation that imported this @Configuration class.
*
* @param importMetadata the AnnotationMetadata of the class importing this @Configuration class.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession
* @param importMetadata the AnnotationMetadata of the class importing
* this @Configuration class.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* EnableGemFireHttpSession
* @see org.springframework.core.type.AnnotationMetadata
*/
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"));
setIndexableSessionAttributes(enableGemFireHttpSessionAnnotationAttributes
.getStringArray("indexableSessionAttributes"));
setMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAnnotationAttributes.getNumber(
"maxInactiveIntervalInSeconds").intValue());
setMaxInactiveIntervalInSeconds(enableGemFireHttpSessionAnnotationAttributes
.getNumber("maxInactiveIntervalInSeconds").intValue());
setServerRegionShortcut(RegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes.getEnum(
"serverRegionShortcut")));
setServerRegionShortcut(
RegionShortcut.class.cast(enableGemFireHttpSessionAnnotationAttributes
.getEnum("serverRegionShortcut")));
setSpringSessionGemFireRegionName(enableGemFireHttpSessionAnnotationAttributes.getString("regionName"));
setSpringSessionGemFireRegionName(
enableGemFireHttpSessionAnnotationAttributes.getString("regionName"));
}
/**
* Defines the Spring SessionRepository bean used to interact with GemFire as a Spring Session provider.
* Defines the Spring SessionRepository bean used to interact with GemFire as a Spring
* Session provider.
*
* @param gemfireOperations an instance of {@link GemfireOperations} used to manage Spring Sessions in GemFire.
* @return a GemFireOperationsSessionRepository for managing (clustering/replicating) Sessions using GemFire.
* @param gemfireOperations an instance of {@link GemfireOperations} used to manage
* Spring Sessions in GemFire.
* @return a GemFireOperationsSessionRepository for managing (clustering/replicating)
* Sessions using GemFire.
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.data.gemfire.GemfireOperations
*/
@Bean
public GemFireOperationsSessionRepository sessionRepository(@Qualifier("sessionRegionTemplate")
GemfireOperations gemfireOperations) {
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;
}
/**
* Defines a Spring GemfireTemplate bean used to interact with GemFire's (Client)Cache {@link Region}
* storing Sessions.
* Defines a Spring GemfireTemplate bean used to interact with GemFire's (Client)Cache
* {@link Region} storing Sessions.
*
* @param gemFireCache reference to the single GemFire cache instance used by the {@link GemfireTemplate}
* to perform GemFire cache data access operations.
* @return a {@link GemfireTemplate} used to interact with GemFire's (Client)Cache {@link Region} storing Sessions.
* @param gemFireCache reference to the single GemFire cache instance used by the
* {@link GemfireTemplate} to perform GemFire cache data access operations.
* @return a {@link GemfireTemplate} used to interact with GemFire's (Client)Cache
* {@link Region} storing Sessions.
* @see org.springframework.data.gemfire.GemfireTemplate
* @see com.gemstone.gemfire.cache.Region
*/
@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()));
}
/**
* Defines a Spring GemFire {@link com.gemstone.gemfire.cache.Cache} {@link Region} bean used to store
* and manage Sessions using either a client-server or peer-to-peer (p2p) topology.
* Defines a Spring GemFire {@link com.gemstone.gemfire.cache.Cache} {@link Region}
* bean used to store and manage Sessions using either a client-server or peer-to-peer
* (p2p) topology.
*
* @param gemfireCache a reference to the GemFire {@link com.gemstone.gemfire.cache.Cache}.
* @param sessionRegionAttributes the GemFire {@link RegionAttributes} used to configure the {@link Region}.
* @return a {@link GemFireCacheTypeAwareRegionFactoryBean} used to configure and initialize a GemFire Cache
* {@link Region} for storing and managing Sessions.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean
* @param gemfireCache a reference to the GemFire
* {@link com.gemstone.gemfire.cache.Cache}.
* @param sessionRegionAttributes the GemFire {@link RegionAttributes} used to
* configure the {@link Region}.
* @return a {@link GemFireCacheTypeAwareRegionFactoryBean} used to configure and
* initialize a GemFire Cache {@link Region} for storing and managing Sessions.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.
* GemFireCacheTypeAwareRegionFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.RegionAttributes
* @see #getClientRegionShortcut()
@@ -351,11 +387,11 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
* @see #getServerRegionShortcut()
*/
@Bean(name = DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME)
public GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion(GemFireCache gemfireCache,
public GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession> sessionRegion(
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());
@@ -367,13 +403,14 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Defines a Spring GemFire {@link RegionAttributes} bean used to configure and initialize the GemFire cache
* {@link Region} storing Sessions. Expiration is also configured for the {@link Region} on the basis that the
* GemFire cache {@link Region} is a not a proxy, on either the client or server.
* Defines a Spring GemFire {@link RegionAttributes} bean used to configure and
* initialize the GemFire cache {@link Region} storing Sessions. Expiration is also
* configured for the {@link Region} on the basis that the GemFire cache
* {@link Region} is a not a proxy, on either the client or server.
*
* @param gemfireCache a reference to the GemFire cache.
* @return an instance of {@link RegionAttributes} used to configure and initialize the GemFire cache {@link Region}
* for storing and managing Sessions.
* @return an instance of {@link RegionAttributes} used to configure and initialize
* the GemFire cache {@link Region} for storing and managing Sessions.
* @see org.springframework.data.gemfire.RegionAttributesFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.PartitionAttributes
@@ -381,44 +418,49 @@ 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;
}
/**
* Determines whether expiration configuration is allowed to be set on the GemFire cache {@link Region}
* used to store and manage Sessions.
* Determines whether expiration configuration is allowed to be set on the GemFire
* cache {@link Region} used to store and manage Sessions.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a boolean indicating if a {@link Region} can be configured for Region entry idle-timeout expiration.
* @return a boolean indicating if a {@link Region} can be configured for Region entry
* idle-timeout expiration.
* @see GemFireUtils#isClient(GemFireCache)
* @see GemFireUtils#isProxy(ClientRegionShortcut)
* @see GemFireUtils#isProxy(RegionShortcut)
*/
boolean isExpirationAllowed(GemFireCache gemfireCache) {
return !(GemFireUtils.isClient(gemfireCache) ? GemFireUtils.isProxy(getClientRegionShortcut())
: GemFireUtils.isProxy(getServerRegionShortcut()));
return !(GemFireUtils.isClient(gemfireCache)
? GemFireUtils.isProxy(getClientRegionShortcut())
: GemFireUtils.isProxy(getServerRegionShortcut()));
}
/**
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and managing Sessions,
* specifically on the 'principalName' property for quick lookup and queries. This index will only be created
* on a server @{link Region}.
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and
* managing Sessions, specifically on the 'principalName' property for quick lookup
* and queries. This index will only be created on a server @{link Region}.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a IndexFactoryBean creating an GemFire Index on the 'principalName' property of Sessions stored
* in the GemFire cache {@link Region}.
* @return a IndexFactoryBean creating an GemFire Index on the 'principalName'
* property of Sessions stored in the GemFire cache {@link Region}.
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
*/
@@ -445,12 +487,14 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
}
/**
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and managing Sessions,
* specifically on Session attributes for quick lookup and queries on Session attribute names with a given value.
* This index will only be created on a server @{link Region}.
* Defines a Spring GemFire Index bean on the GemFire cache {@link Region} storing and
* managing Sessions, specifically on Session attributes for quick lookup and queries
* on Session attribute names with a given value. This index will only be created on a
* server @{link Region}.
*
* @param gemfireCache a reference to the GemFire cache.
* @return a IndexFactoryBean creating an GemFire Index on attributes of Sessions stored in the GemFire cache {@link Region}.
* @return a IndexFactoryBean creating an GemFire Index on attributes of Sessions
* stored in the GemFire cache {@link Region}.
* @see org.springframework.data.gemfire.IndexFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
*/
@@ -460,7 +504,8 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
IndexFactoryBean index = new IndexFactoryBean() {
@Override
public void afterPropertiesSet() throws Exception {
if (GemFireUtils.isPeer(gemfireCache) && !ObjectUtils.isEmpty(getIndexableSessionAttributes())) {
if (GemFireUtils.isPeer(gemfireCache)
&& !ObjectUtils.isEmpty(getIndexableSessionAttributes())) {
super.afterPropertiesSet();
}
}
@@ -468,8 +513,10 @@ public class GemFireHttpSessionConfiguration extends SpringHttpSessionConfigurat
index.setCache(gemfireCache);
index.setName("sessionAttributesIndex");
index.setExpression(String.format("s.attributes[%1$s]", getIndexableSessionAttributesAsGemFireIndexExpression()));
index.setFrom(String.format("%1$s s", GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName())));
index.setExpression(String.format("s.attributes[%1$s]",
getIndexableSessionAttributesAsGemFireIndexExpression()));
index.setFrom(String.format("%1$s s",
GemFireUtils.toRegionPath(getSpringSessionGemFireRegionName())));
index.setOverride(true);
return index;

View File

@@ -34,8 +34,9 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* The GemFireCacheTypeAwareRegionFactoryBean class is a Spring {@link FactoryBean} used to construct, configure
* and initialize the GemFire cache {@link Region} used to store and manage Session state.
* The GemFireCacheTypeAwareRegionFactoryBean class is a Spring {@link FactoryBean} used
* to construct, configure and initialize the GemFire cache {@link Region} used to store
* and manage Session state.
*
* @param <K> the type of keys
* @param <V> the type of values
@@ -45,7 +46,8 @@ import org.springframework.util.StringUtils;
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* GemFireHttpSessionConfiguration
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.InterestResultPolicy
* @see com.gemstone.gemfire.cache.Region
@@ -53,16 +55,14 @@ import org.springframework.util.StringUtils;
* @see com.gemstone.gemfire.cache.RegionShortcut
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean<Region<K, V>>, InitializingBean {
public class GemFireCacheTypeAwareRegionFactoryBean<K, V>
implements FactoryBean<Region<K, V>>, InitializingBean {
protected static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT =
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT;
protected static final ClientRegionShortcut DEFAULT_CLIENT_REGION_SHORTCUT = GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT;
protected static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT =
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT;
protected static final RegionShortcut DEFAULT_SERVER_REGION_SHORTCUT = GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT;
protected static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME =
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
protected static final String DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME = GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME;
private ClientRegionShortcut clientRegionShortcut;
@@ -77,9 +77,10 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
private String regionName;
/**
* Post-construction initialization callback to create, configure and initialize the GemFire cache {@link Region}
* used to store, replicate (distribute) and manage Session state. This method intelligently handles
* both client-server and peer-to-peer (p2p) GemFire supported distributed system topologies.
* Post-construction initialization callback to create, configure and initialize the
* GemFire cache {@link Region} used to store, replicate (distribute) and manage
* Session state. This method intelligently handles both client-server and
* peer-to-peer (p2p) GemFire supported distributed system topologies.
*
* @throws Exception if the initialization of the GemFire cache {@link Region} fails.
* @see org.springframework.session.data.gemfire.support.GemFireUtils#isClient(GemFireCache)
@@ -91,17 +92,20 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
GemFireCache gemfireCache = getGemfireCache();
this.region = (GemFireUtils.isClient(gemfireCache) ? newClientRegion(gemfireCache)
: newServerRegion(gemfireCache));
: newServerRegion(gemfireCache));
}
/**
* Constructs a GemFire cache {@link Region} using a peer-to-peer (p2p) GemFire topology to store
* and manage Session state in a GemFire server cluster accessible from a GemFire cache client.
* Constructs a GemFire cache {@link Region} using a peer-to-peer (p2p) GemFire
* topology to store and manage Session state in a GemFire server cluster accessible
* from a GemFire cache client.
*
* @param gemfireCache a reference to the GemFire {@link com.gemstone.gemfire.cache.Cache}.
* @return a peer-to-peer-based GemFire cache {@link Region} to store and manage Session state.
* @throws Exception if the instantiation, configuration and initialization
* of the GemFire cache {@link Region} fails.
* @param gemfireCache a reference to the GemFire
* {@link com.gemstone.gemfire.cache.Cache}.
* @return a peer-to-peer-based GemFire cache {@link Region} to store and manage
* Session state.
* @throws Exception if the instantiation, configuration and initialization of the
* GemFire cache {@link Region} fails.
* @see org.springframework.data.gemfire.GenericRegionFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
@@ -122,13 +126,16 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Constructs a GemFire cache {@link Region} using the client-server GemFire topology to store
* and manage Session state in a GemFire server cluster accessible from a GemFire cache client.
* Constructs a GemFire cache {@link Region} using the client-server GemFire topology
* to store and manage Session state in a GemFire server cluster accessible from a
* GemFire cache client.
*
* @param gemfireCache a reference to the GemFire {@link com.gemstone.gemfire.cache.Cache}.
* @return a client-server-based GemFire cache {@link Region} to store and manage Session state.
* @throws Exception if the instantiation, configuration and initialization
* of the GemFire cache {@link Region} fails.
* @param gemfireCache a reference to the GemFire
* {@link com.gemstone.gemfire.cache.Cache}.
* @return a client-server-based GemFire cache {@link Region} to store and manage
* Session state.
* @throws Exception if the instantiation, configuration and initialization of the
* GemFire cache {@link Region} fails.
* @see org.springframework.data.gemfire.client.ClientRegionFactoryBean
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
@@ -155,12 +162,14 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
/**
* Registers interests in all keys when the client {@link Region} is non-local.
*
* @return an array of Interests specifying the server notifications of interests to the client.
* @return an array of Interests specifying the server notifications of interests to
* the client.
* @see org.springframework.data.gemfire.client.Interest
*/
/**
* Decides whether interests will be registered for all keys. Interests is only registered on a client
* and typically only when the client is a (CACHING) PROXY to the server (i.e. non-LOCAL only).
* Decides whether interests will be registered for all keys. Interests is only
* registered on a client and typically only when the client is a (CACHING) PROXY to
* the server (i.e. non-LOCAL only).
*
* @param register a boolean value indicating whether interests should be registered.
* @return an array of Interests KEY/VALUE registrations.
@@ -168,13 +177,14 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
*/
@SuppressWarnings("unchecked")
protected Interest<K>[] registerInterests(boolean register) {
return (!register ? new Interest[0] : new Interest[] {
new Interest<String>("ALL_KEYS", InterestResultPolicy.KEYS)
});
return (!register ? new Interest[0]
: new Interest[] {
new Interest<String>("ALL_KEYS", InterestResultPolicy.KEYS) });
}
/**
* Returns a reference to the constructed GemFire cache {@link Region} used to store and manage Session state.
* Returns a reference to the constructed GemFire cache {@link Region} used to store
* and manage Session state.
*
* @return the {@link Region} used to store and manage Session state.
* @throws Exception if the {@link Region} reference cannot be obtained.
@@ -185,8 +195,8 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns the specific type of GemFire cache {@link Region} this factory creates when initialized
* or Region.class when uninitialized.
* Returns the specific type of GemFire cache {@link Region} this factory creates when
* initialized or Region.class when uninitialized.
*
* @return the GemFire cache {@link Region} class type constructed by this factory.
* @see com.gemstone.gemfire.cache.Region
@@ -197,19 +207,22 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns true indicating the GemFire cache {@link Region} created by this factory is the sole instance.
* Returns true indicating the GemFire cache {@link Region} created by this factory is
* the sole instance.
*
* @return true to indicate the GemFire cache {@link Region} storing and managing Sessions is a Singleton.
* @return true to indicate the GemFire cache {@link Region} storing and managing
* Sessions is a Singleton.
*/
public boolean isSingleton() {
return true;
}
/**
* Sets the {@link Region} data policy used by the GemFire cache client to manage Session state.
* Sets the {@link Region} data policy used by the GemFire cache client to manage
* Session state.
*
* @param clientRegionShortcut a {@link ClientRegionShortcut} to specify the client {@link Region}
* data management policy.
* @param clientRegionShortcut a {@link ClientRegionShortcut} to specify the client
* {@link Region} data management policy.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
public void setClientRegionShortcut(ClientRegionShortcut clientRegionShortcut) {
@@ -217,19 +230,22 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns the {@link Region} data policy used by the GemFire cache client to manage Session state. Defaults to
* {@link ClientRegionShortcut#PROXY}.
* Returns the {@link Region} data policy used by the GemFire cache client to manage
* Session state. Defaults to {@link ClientRegionShortcut#PROXY}.
*
* @return a {@link ClientRegionShortcut} specifying the client {@link Region} data management policy.
* @return a {@link ClientRegionShortcut} specifying the client {@link Region} data
* management policy.
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration#DEFAULT_CLIENT_REGION_SHORTCUT
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
protected ClientRegionShortcut getClientRegionShortcut() {
return (this.clientRegionShortcut != null ? this.clientRegionShortcut : DEFAULT_CLIENT_REGION_SHORTCUT);
return (this.clientRegionShortcut != null ? this.clientRegionShortcut
: DEFAULT_CLIENT_REGION_SHORTCUT);
}
/**
* Sets a reference to the GemFire cache used to construct the appropriate {@link Region}.
* Sets a reference to the GemFire cache used to construct the appropriate
* {@link Region}.
*
* @param gemfireCache a reference to the GemFire cache.
* @throws IllegalArgumentException if the {@link GemFireCache} reference is null.
@@ -240,21 +256,24 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns a reference to the GemFire cache used to construct the appropriate {@link Region}.
* Returns a reference to the GemFire cache used to construct the appropriate
* {@link Region}.
*
* @return a reference to the GemFire cache.
* @throws IllegalStateException if the {@link GemFireCache} reference is null.
*/
protected GemFireCache getGemfireCache() {
Assert.state(this.gemfireCache != null, "A reference to a GemFireCache was not properly configured");
Assert.state(this.gemfireCache != null,
"A reference to a GemFireCache was not properly configured");
return this.gemfireCache;
}
/**
* Sets the GemFire {@link RegionAttributes} used to configure the GemFire cache {@link Region} used to
* store and manage Session state.
* Sets the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region} used to store and manage Session state.
*
* @param regionAttributes the GemFire {@link RegionAttributes} used to configure the GemFire cache {@link Region}.
* @param regionAttributes the GemFire {@link RegionAttributes} used to configure the
* GemFire cache {@link Region}.
* @see com.gemstone.gemfire.cache.RegionAttributes
*/
public void setRegionAttributes(RegionAttributes<K, V> regionAttributes) {
@@ -262,10 +281,11 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns the GemFire {@link RegionAttributes} used to configure the GemFire cache {@link Region} used to
* store and manage Session state.
* Returns the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region} used to store and manage Session state.
*
* @return the GemFire {@link RegionAttributes} used to configure the GemFire cache {@link Region}.
* @return the GemFire {@link RegionAttributes} used to configure the GemFire cache
* {@link Region}.
* @see com.gemstone.gemfire.cache.RegionAttributes
*/
protected RegionAttributes<K, V> getRegionAttributes() {
@@ -273,7 +293,8 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Sets the name of the GemFire cache {@link Region} use to store and manage Session state.
* Sets the name of the GemFire cache {@link Region} use to store and manage Session
* state.
*
* @param regionName a String specifying the name of the GemFire cache {@link Region}.
*/
@@ -282,20 +303,23 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns the configured name of the GemFire cache {@link Region} use to store and manage Session state.
* Defaults to "ClusteredSpringSessions"
* Returns the configured name of the GemFire cache {@link Region} use to store and
* manage Session state. Defaults to "ClusteredSpringSessions"
*
* @return a String specifying the name of the GemFire cache {@link Region}.
* @see com.gemstone.gemfire.cache.Region#getName()
*/
protected String getRegionName() {
return (StringUtils.hasText(this.regionName) ? this.regionName : DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
return (StringUtils.hasText(this.regionName) ? this.regionName
: DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
/**
* Sets the {@link Region} data policy used by the GemFire peer cache to manage Session state.
* Sets the {@link Region} data policy used by the GemFire peer cache to manage
* Session state.
*
* @param serverRegionShortcut a {@link RegionShortcut} to specify the peer {@link Region} data management policy.
* @param serverRegionShortcut a {@link RegionShortcut} to specify the peer
* {@link Region} data management policy.
* @see com.gemstone.gemfire.cache.RegionShortcut
*/
public void setServerRegionShortcut(RegionShortcut serverRegionShortcut) {
@@ -303,14 +327,16 @@ public class GemFireCacheTypeAwareRegionFactoryBean<K, V> implements FactoryBean
}
/**
* Returns the {@link Region} data policy used by the GemFire peer cache to manage Session state. Defaults to
* {@link RegionShortcut#PARTITION}.
* Returns the {@link Region} data policy used by the GemFire peer cache to manage
* Session state. Defaults to {@link RegionShortcut#PARTITION}.
*
* @return a {@link RegionShortcut} specifying the peer {@link Region} data management policy.
* @return a {@link RegionShortcut} specifying the peer {@link Region} data management
* policy.
* @see com.gemstone.gemfire.cache.RegionShortcut
*/
protected RegionShortcut getServerRegionShortcut() {
return (this.serverRegionShortcut != null ? this.serverRegionShortcut : DEFAULT_SERVER_REGION_SHORTCUT);
return (this.serverRegionShortcut != null ? this.serverRegionShortcut
: DEFAULT_SERVER_REGION_SHORTCUT);
}
}

View File

@@ -28,8 +28,8 @@ import com.gemstone.gemfire.cache.client.ClientRegionShortcut;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
/**
* GemFireUtils is an abstract, extensible utility class for working with GemFire types and functionality
* and is used by Spring Session's GemFire adapter support classes.
* GemFireUtils is an abstract, extensible utility class for working with GemFire types
* and functionality and is used by Spring Session's GemFire adapter support classes.
*
* @author John Blum
* @since 1.1.0
@@ -40,8 +40,8 @@ public abstract class GemFireUtils {
* Null-safe method to close the given {@link Closeable} object.
*
* @param obj the {@link Closeable} object to close.
* @return true if the {@link Closeable} object is not null and was successfully closed,
* otherwise return false.
* @return true if the {@link Closeable} object is not null and was successfully
* closed, otherwise return false.
* @see java.io.Closeable
*/
public static boolean close(Closeable obj) {
@@ -67,7 +67,8 @@ public abstract class GemFireUtils {
*/
public static boolean isClient(GemFireCache gemFireCache) {
boolean client = (gemFireCache instanceof ClientCache);
client &= (!(gemFireCache instanceof GemFireCacheImpl) || ((GemFireCacheImpl) gemFireCache).isClient());
client &= (!(gemFireCache instanceof GemFireCacheImpl)
|| ((GemFireCacheImpl) gemFireCache).isClient());
return client;
}
@@ -87,56 +88,58 @@ public abstract class GemFireUtils {
* Determines whether the given {@link ClientRegionShortcut} is local only.
*
* @param shortcut the ClientRegionShortcut to evaluate.
* @return a boolean value indicating if the {@link ClientRegionShortcut} is local or not.
* @return a boolean value indicating if the {@link ClientRegionShortcut} is local or
* not.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
public static boolean isLocal(ClientRegionShortcut shortcut) {
switch (shortcut) {
case LOCAL:
case LOCAL_HEAP_LRU:
case LOCAL_OVERFLOW:
case LOCAL_PERSISTENT:
case LOCAL_PERSISTENT_OVERFLOW:
return true;
default:
return false;
case LOCAL:
case LOCAL_HEAP_LRU:
case LOCAL_OVERFLOW:
case LOCAL_PERSISTENT:
case LOCAL_PERSISTENT_OVERFLOW:
return true;
default:
return false;
}
}
/**
* Determines whether the client {@link ClientRegionShortcut} is a proxy-based shortcut.
* NOTE: "proxy"-based Regions keep no local state.
* Determines whether the client {@link ClientRegionShortcut} is a proxy-based
* shortcut. NOTE: "proxy"-based Regions keep no local state.
*
* @param shortcut the client {@link ClientRegionShortcut} to evaluate.
* @return a boolean value indicating whether the client {@link ClientRegionShortcut} refers to
* a proxy-based shortcut.
* @return a boolean value indicating whether the client {@link ClientRegionShortcut}
* refers to a proxy-based shortcut.
* @see com.gemstone.gemfire.cache.client.ClientRegionShortcut
*/
public static boolean isProxy(ClientRegionShortcut shortcut) {
switch (shortcut) {
case PROXY:
return true;
default:
return false;
case PROXY:
return true;
default:
return false;
}
}
/**
* Determines whether the peer {@link RegionShortcut} is a proxy-based shortcut. NOTE: "proxy"-based Regions
* keep no local state.
* Determines whether the peer {@link RegionShortcut} is a proxy-based shortcut. NOTE:
* "proxy"-based Regions keep no local state.
*
* @param shortcut the peer {@link RegionShortcut} to evaluate.
* @return a boolean value indicating whether the peer {@link RegionShortcut} refers to a proxy-based shortcut.
* @return a boolean value indicating whether the peer {@link RegionShortcut} refers
* to a proxy-based shortcut.
* @see com.gemstone.gemfire.cache.RegionShortcut
*/
public static boolean isProxy(RegionShortcut shortcut) {
switch (shortcut) {
case PARTITION_PROXY:
case PARTITION_PROXY_REDUNDANT:
case REPLICATE_PROXY:
return true;
default:
return false;
case PARTITION_PROXY:
case PARTITION_PROXY_REDUNDANT:
case REPLICATE_PROXY:
return true;
default:
return false;
}
}

View File

@@ -1,7 +1,25 @@
/*
* 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;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.IndexOperations;
@@ -9,12 +27,10 @@ import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
/**
* Base class for serializing and deserializing session objects.
* To create custom serializer you have to implement this interface
* and simply register your class as a bean.
* Base class for serializing and deserializing session objects. To create custom
* serializer you have to implement this interface and simply register your class as a
* bean.
*
* @author Jakub Kubrynski
* @since 1.2
@@ -26,7 +42,7 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter
protected static final String EXPIRE_AT_FIELD_NAME = "expireAt";
/**
* Returns query to be executed to return sessions based on a particular index
* Returns query to be executed to return sessions based on a particular index.
* @param indexName name of the index
* @param indexValue value to query against
* @return built query or null if indexName is not supported
@@ -34,23 +50,26 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter
protected abstract Query getQueryForIndex(String indexName, Object indexValue);
/**
* Method ensures that there is a TTL index on {@literal expireAt} field.
* It's has {@literal expireAfterSeconds} set to zero seconds, so the expiration
* time is controlled by the application.
* Method ensures that there is a TTL index on {@literal expireAt} field. It's has
* {@literal expireAfterSeconds} set to zero seconds, so the expiration time is
* controlled by the application.
*
* It can be extended in custom converters when there is a need for creating
* additional custom indexes.
* @param sessionCollectionIndexes {@link IndexOperations} to use
*/
protected void ensureIndexes(IndexOperations sessionCollectionIndexes) {
List<IndexInfo> indexInfo = sessionCollectionIndexes.getIndexInfo();
for (IndexInfo info : indexInfo) {
if (EXPIRE_AT_FIELD_NAME.equals(info.getName())) {
LOG.debug("TTL index on field " + EXPIRE_AT_FIELD_NAME + " already exists");
LOG.debug(
"TTL index on field " + EXPIRE_AT_FIELD_NAME + " already exists");
return;
}
}
LOG.info("Creating TTL index on field " + EXPIRE_AT_FIELD_NAME);
sessionCollectionIndexes
.ensureIndex(new Index(EXPIRE_AT_FIELD_NAME, Sort.Direction.ASC).named(EXPIRE_AT_FIELD_NAME).expire(0));
.ensureIndex(new Index(EXPIRE_AT_FIELD_NAME, Sort.Direction.ASC)
.named(EXPIRE_AT_FIELD_NAME).expire(0));
}
}

View File

@@ -1,21 +1,36 @@
/*
* 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;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Utility class to extract principal name from {@code Authentication} object
* Utility class to extract principal name from {@code Authentication} object.
*
* @author Jakub Kubrynski
*/
class AuthenticationParser {
final class AuthenticationParser {
private static final String NAME_EXPRESSION = "authentication?.name";
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
/**
* Extracts principal name from authentication
* Extracts principal name from authentication.
*
* @param authentication Authentication object
* @return principal name
@@ -27,4 +42,7 @@ class AuthenticationParser {
}
return null;
}
}
private AuthenticationParser() {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -15,15 +15,6 @@
*/
package org.springframework.session.data.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.session.Session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -35,11 +26,20 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
/**
* {@code AbstractMongoSessionConverter} implementation transforming {@code MongoExpiringSession} to/from a BSON object
* using standard Java serialization
* {@code AbstractMongoSessionConverter} implementation transforming.
* {@code MongoExpiringSession} to/from a BSON object using standard Java serialization
*
* @author Jakub Kubrynski
* @since 1.2
@@ -57,25 +57,30 @@ class JdkMongoSessionConverter extends AbstractMongoSessionConverter {
private static final String PRINCIPAL_FIELD_NAME = "principal";
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@Override
public Query getQueryForIndex(String indexName, Object indexValue) {
if (PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
if (FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME
.equals(indexName)) {
return Query.query(Criteria.where(PRINCIPAL_FIELD_NAME).is(indexValue));
}
return null;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(DBObject.class, MongoExpiringSession.class));
return Collections.singleton(
new ConvertiblePair(DBObject.class, MongoExpiringSession.class));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
public Object convert(Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
if (source == null) {
return null;
}
if (DBObject.class.isAssignableFrom(sourceType.getType())) {
return convert((DBObject) source);
} else {
}
else {
return convert((MongoExpiringSession) source);
}
}
@@ -103,24 +108,29 @@ class JdkMongoSessionConverter extends AbstractMongoSessionConverter {
outputStream.writeObject(attributes);
outputStream.flush();
return out.toByteArray();
} catch (IOException e) {
}
catch (IOException e) {
LOG.error("Exception during session serialization", e);
throw new IllegalStateException("Cannot serialize session", e);
}
}
private String extractPrincipal(Session expiringSession) {
String resolvedPrincipal = AuthenticationParser.extractName(expiringSession.getAttribute(SPRING_SECURITY_CONTEXT));
String resolvedPrincipal = AuthenticationParser
.extractName(expiringSession.getAttribute(SPRING_SECURITY_CONTEXT));
if (resolvedPrincipal != null) {
return resolvedPrincipal;
} else {
return expiringSession.getAttribute(PRINCIPAL_NAME_INDEX_NAME);
}
else {
return expiringSession.getAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
}
}
private MongoExpiringSession convert(DBObject sessionWrapper) {
MongoExpiringSession session =
new MongoExpiringSession((String) sessionWrapper.get(ID), (Integer) sessionWrapper.get(MAX_INTERVAL));
MongoExpiringSession session = new MongoExpiringSession(
(String) sessionWrapper.get(ID),
(Integer) sessionWrapper.get(MAX_INTERVAL));
session.setCreationTime((Long) sessionWrapper.get(CREATION_TIME));
session.setLastAccessedTime((Long) sessionWrapper.get(LAST_ACCESSED_TIME));
session.setExpireAt((Date) sessionWrapper.get(EXPIRE_AT_FIELD_NAME));
@@ -131,17 +141,21 @@ class JdkMongoSessionConverter extends AbstractMongoSessionConverter {
@SuppressWarnings("unchecked")
private void deserializeAttributes(DBObject sessionWrapper, Session session) {
try {
ByteArrayInputStream in = new ByteArrayInputStream((byte[]) sessionWrapper.get(ATTRIBUTES));
ByteArrayInputStream in = new ByteArrayInputStream(
(byte[]) sessionWrapper.get(ATTRIBUTES));
ObjectInputStream objectInputStream = new ObjectInputStream(in);
Map<String, Object> attributes = (Map<String, Object>) objectInputStream.readObject();
Map<String, Object> attributes = (Map<String, Object>) objectInputStream
.readObject();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
session.setAttribute(entry.getKey(), entry.getValue());
}
objectInputStream.close();
} catch (IOException e) {
}
catch (IOException e) {
LOG.error("Exception during session deserialization", e);
throw new IllegalStateException("Cannot deserialize session", e);
} catch (ClassNotFoundException e) {
}
catch (ClassNotFoundException e) {
LOG.error("Exception during session deserialization", e);
throw new IllegalStateException("Cannot deserialize session", e);
}

View File

@@ -1,7 +1,20 @@
/*
* 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;
import org.springframework.session.ExpiringSession;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -9,9 +22,10 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.springframework.session.ExpiringSession;
/**
* Session object providing additional information about
* the datetime of expiration
* Session object providing additional information about the datetime of expiration.
*
* @author Jakub Kubrynski
* @since 1.2
@@ -36,36 +50,37 @@ public class MongoExpiringSession implements ExpiringSession {
public MongoExpiringSession(String id, int maxInactiveIntervalInSeconds) {
this.id = id;
this.interval = maxInactiveIntervalInSeconds;
setLastAccessedTime(created);
setLastAccessedTime(this.created);
}
public String getId() {
return id;
return this.id;
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String attributeName) {
return (T) attrs.get(attributeName);
return (T) this.attrs.get(attributeName);
}
public Set<String> getAttributeNames() {
return attrs.keySet();
return this.attrs.keySet();
}
public void setAttribute(String attributeName, Object attributeValue) {
if (attributeValue == null) {
removeAttribute(attributeName);
} else {
attrs.put(attributeName, attributeValue);
}
else {
this.attrs.put(attributeName, attributeValue);
}
}
public void removeAttribute(String attributeName) {
attrs.remove(attributeName);
this.attrs.remove(attributeName);
}
public long getCreationTime() {
return created;
return this.created;
}
public void setCreationTime(long created) {
@@ -74,11 +89,12 @@ public class MongoExpiringSession implements ExpiringSession {
public void setLastAccessedTime(long lastAccessedTime) {
this.accessed = lastAccessedTime;
expireAt = new Date(lastAccessedTime + TimeUnit.SECONDS.toMillis(interval));
this.expireAt = new Date(
lastAccessedTime + TimeUnit.SECONDS.toMillis(this.interval));
}
public long getLastAccessedTime() {
return accessed;
return this.accessed;
}
public void setMaxInactiveIntervalInSeconds(int interval) {
@@ -86,32 +102,38 @@ public class MongoExpiringSession implements ExpiringSession {
}
public int getMaxInactiveIntervalInSeconds() {
return interval;
return this.interval;
}
public boolean isExpired() {
return new Date().after(expireAt);
return new Date().after(this.expireAt);
}
public Date getExpireAt() {
return expireAt;
return this.expireAt;
}
public void setExpireAt(Date expireAt) {
this.expireAt = expireAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MongoExpiringSession that = (MongoExpiringSession) o;
return id.equals(that.id);
return this.id.equals(that.id);
}
@Override
public int hashCode() {
return id.hashCode();
return this.id.hashCode();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -15,33 +15,43 @@
*/
package org.springframework.session.data.mongo;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import com.mongodb.DBObject;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.session.FindByIndexNameSessionRepository;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Session repository implementation which stores sessions in Mongo.
* Uses {@link AbstractMongoSessionConverter} to transform session objects from/to
* native Mongo representation ({@code DBObject}).
* Session repository implementation which stores sessions in Mongo. Uses
* {@link AbstractMongoSessionConverter} to transform session objects from/to native Mongo
* representation ({@code DBObject}).
*
* Repository is also responsible for removing expired sessions from database.
* Cleanup is done every minute.
* Repository is also responsible for removing expired sessions from database. Cleanup is
* done every minute.
*
* @author Jakub Kubrynski
* @since 1.2
*/
public class MongoOperationsSessionRepository implements FindByIndexNameSessionRepository<MongoExpiringSession> {
public class MongoOperationsSessionRepository
implements FindByIndexNameSessionRepository<MongoExpiringSession> {
/**
* The default time period in seconds in which a session will expire.
*/
public static final int DEFAULT_INACTIVE_INTERVAL = 1800;
/**
* the default collection name for storing session.
*/
public static final String DEFAULT_COLLECTION_NAME = "sessions";
private final MongoOperations mongoOperations;
@@ -55,12 +65,12 @@ public class MongoOperationsSessionRepository implements FindByIndexNameSessionR
}
public MongoExpiringSession createSession() {
return new MongoExpiringSession(maxInactiveIntervalInSeconds);
return new MongoExpiringSession(this.maxInactiveIntervalInSeconds);
}
public void save(MongoExpiringSession session) {
DBObject sessionDbObject = convertToDBObject(session);
mongoOperations.getCollection(collectionName).save(sessionDbObject);
this.mongoOperations.getCollection(this.collectionName).save(sessionDbObject);
}
public MongoExpiringSession getSession(String id) {
@@ -77,19 +87,23 @@ public class MongoOperationsSessionRepository implements FindByIndexNameSessionR
}
/**
* Currently this repository allows only querying against {@code PRINCIPAL_NAME_INDEX_NAME}
* Currently this repository allows only querying against
* {@code PRINCIPAL_NAME_INDEX_NAME}.
*
* @param indexName the name if the index (i.e. {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
* @param indexName the name if the index (i.e.
* {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME})
* @param indexValue the value of the index to search for.
* @return sessions map
*/
public Map<String, MongoExpiringSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
public Map<String, MongoExpiringSession> findByIndexNameAndIndexValue(
String indexName, String indexValue) {
HashMap<String, MongoExpiringSession> result = new HashMap<String, MongoExpiringSession>();
Query query = mongoSessionConverter.getQueryForIndex(indexName, indexValue);
Query query = this.mongoSessionConverter.getQueryForIndex(indexName, indexValue);
if (query == null) {
return Collections.emptyMap();
}
List<DBObject> mapSessions = mongoOperations.find(query, DBObject.class, collectionName);
List<DBObject> mapSessions = this.mongoOperations.find(query, DBObject.class,
this.collectionName);
for (DBObject dbSession : mapSessions) {
MongoExpiringSession mapSession = convertToSession(dbSession);
result.put(mapSession.getId(), mapSession);
@@ -98,30 +112,34 @@ public class MongoOperationsSessionRepository implements FindByIndexNameSessionR
}
public void delete(String id) {
mongoOperations.remove(findSession(id), collectionName);
this.mongoOperations.remove(findSession(id), this.collectionName);
}
@PostConstruct
public void ensureIndexesAreCreated() {
IndexOperations indexOperations = mongoOperations.indexOps(collectionName);
mongoSessionConverter.ensureIndexes(indexOperations);
IndexOperations indexOperations = this.mongoOperations
.indexOps(this.collectionName);
this.mongoSessionConverter.ensureIndexes(indexOperations);
}
DBObject findSession(String id) {
return mongoOperations.findById(id, DBObject.class, collectionName);
return this.mongoOperations.findById(id, DBObject.class, this.collectionName);
}
MongoExpiringSession convertToSession(DBObject session) {
return (MongoExpiringSession) mongoSessionConverter.convert(session,
TypeDescriptor.valueOf(DBObject.class), TypeDescriptor.valueOf(MongoExpiringSession.class));
return (MongoExpiringSession) this.mongoSessionConverter.convert(session,
TypeDescriptor.valueOf(DBObject.class),
TypeDescriptor.valueOf(MongoExpiringSession.class));
}
DBObject convertToDBObject(MongoExpiringSession session) {
return (DBObject) mongoSessionConverter.convert(session,
TypeDescriptor.valueOf(MongoExpiringSession.class), TypeDescriptor.valueOf(DBObject.class));
return (DBObject) this.mongoSessionConverter.convert(session,
TypeDescriptor.valueOf(MongoExpiringSession.class),
TypeDescriptor.valueOf(DBObject.class));
}
public void setMongoSessionConverter(AbstractMongoSessionConverter mongoSessionConverter) {
public void setMongoSessionConverter(
AbstractMongoSessionConverter mongoSessionConverter) {
this.mongoSessionConverter = mongoSessionConverter;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -15,22 +15,21 @@
*/
package org.springframework.session.data.mongo.config.annotation.web.http;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
/**
* Add this annotation to a {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and
* backed by Mongo. Use {@code collectionName} to change default name of the
* collection used to store sessions.
* <pre>
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
* Mongo. Use {@code collectionName} to change default name of the collection used to
* store sessions. <pre>
* <code>
* {@literal @EnableMongoHttpSession}
* public class MongoHttpSessionConfig {
@@ -41,8 +40,7 @@ import java.lang.annotation.Target;
* }
*
* }
* </code>
* </pre>
* </code> </pre>
*
* @author Jakub Kubrynski
* @since 1.2
@@ -55,12 +53,16 @@ import java.lang.annotation.Target;
public @interface EnableMongoHttpSession {
/**
* The maximum time a session will be kept if it is inactive.
*
* @return default max inactive interval in seconds
*/
int maxInactiveIntervalInSeconds() default MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL;
/**
* The collection name to use.
*
* @return name of the collection to store session
*/
String collectionName() default MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -23,18 +23,19 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
import org.springframework.session.data.mongo.AbstractMongoSessionConverter;
import org.springframework.session.data.mongo.MongoOperationsSessionRepository;
/**
* Configuration class registering {@code MongoSessionRepository} bean
* To import this configuration use {@link @EnableMongoHttpSession} annotation
* Configuration class registering {@code MongoSessionRepository} bean. To import this
* configuration use {@link EnableMongoHttpSession} annotation.
*
* @author Jakub Kubrynski
* @since 1.2
*/
@Configuration
class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
private AbstractMongoSessionConverter mongoSessionConverter;
@@ -42,25 +43,29 @@ class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguration imple
private String collectionName;
@Bean
MongoOperationsSessionRepository mongoSessionRepository(MongoOperations mongoOperations) {
MongoOperationsSessionRepository repository = new MongoOperationsSessionRepository(mongoOperations);
repository.setCollectionName(collectionName);
repository.setMaxInactiveIntervalInSeconds(maxInactiveIntervalInSeconds);
if (mongoSessionConverter != null) {
repository.setMongoSessionConverter(mongoSessionConverter);
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);
}
return repository;
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableMongoHttpSession.class.getName()));
maxInactiveIntervalInSeconds = attributes.getNumber("maxInactiveIntervalInSeconds");
collectionName = attributes.getString("collectionName");
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importMetadata
.getAnnotationAttributes(EnableMongoHttpSession.class.getName()));
this.maxInactiveIntervalInSeconds = attributes
.getNumber("maxInactiveIntervalInSeconds");
this.collectionName = attributes.getString("collectionName");
}
@Autowired(required = false)
public void setMongoSessionConverter(AbstractMongoSessionConverter mongoSessionConverter) {
public void setMongoSessionConverter(
AbstractMongoSessionConverter mongoSessionConverter) {
this.mongoSessionConverter = mongoSessionConverter;
}
}

View File

@@ -27,17 +27,16 @@ import org.springframework.session.SessionRepository;
public enum RedisFlushMode {
/**
* Only writes to Redis when
* {@link SessionRepository#save(org.springframework.session.Session)} is
* invoked. In a web environment this is typically done as soon as the HTTP
* response is committed.
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
* a web environment this is typically done as soon as the HTTP response is committed.
*/
ON_SAVE,
/**
* Writes to Redis as soon as possible. For example
* {@link SessionRepository#createSession()} will write the session to
* Redis. Another example is that setting an attribute on the session will
* also write to Redis immediately.
* {@link SessionRepository#createSession()} will write the session to Redis. Another
* example is that setting an attribute on the session will also write to Redis
* immediately.
*/
IMMEDIATE
}

View File

@@ -52,12 +52,11 @@ import org.springframework.util.Assert;
/**
* <p>
* A {@link org.springframework.session.SessionRepository} that is implemented
* using Spring Data's
* {@link org.springframework.data.redis.core.RedisOperations}. In a web
* environment, this is typically used in combination with
* {@link SessionRepositoryFilter}. This implementation supports
* {@link SessionDeletedEvent} and {@link SessionExpiredEvent} by implementing {@link MessageListener}.
* A {@link org.springframework.session.SessionRepository} that is implemented using
* Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web
* environment, this is typically used in combination with {@link SessionRepositoryFilter}
* . This implementation supports {@link SessionDeletedEvent} and
* {@link SessionExpiredEvent} by implementing {@link MessageListener}.
* </p>
*
* <h2>Creating a new instance</h2>
@@ -72,16 +71,15 @@ import org.springframework.util.Assert;
*
* <p>
* For additional information on how to create a RedisTemplate, refer to the
* <a href =
* "http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/" >
* <a href = "http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/" >
* Spring Data Redis Reference</a>.
* </p>
*
* <h2>Storage Details</h2>
*
* The sections below outline how Redis is updated for each operation. An
* example of creating a new session can be found below. The subsequent sections
* describe the details.
* The sections below outline how Redis is updated for each operation. An example of
* creating a new session can be found below. The subsequent sections describe the
* details.
*
* <pre>
* HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
@@ -96,9 +94,9 @@ import org.springframework.util.Assert;
*
* <p>
* Each session is stored in Redis as a
* <a href="http://redis.io/topics/data-types#hashes">Hash</a>. Each session is
* set and updated using the <a href="http://redis.io/commands/hmset">HMSET
* command</a>. An example of how each session is stored can be seen below.
* <a href="http://redis.io/topics/data-types#hashes">Hash</a>. Each session is set and
* updated using the <a href="http://redis.io/commands/hmset">HMSET command</a>. An
* example of how each session is stored can be seen below.
* </p>
*
* <pre>
@@ -110,25 +108,24 @@ import org.springframework.util.Assert;
* </p>
* <ul>
* <li>The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe</li>
* <li>The session was created at 1404360000000 in milliseconds since midnight
* of 1/1/1970 GMT.</li>
* <li>The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970
* GMT.</li>
* <li>The session expires in 1800 seconds (30 minutes).</li>
* <li>The session was last accessed at 1404360000000 in milliseconds since
* midnight of 1/1/1970 GMT.</li>
* <li>The session was last accessed at 1404360000000 in milliseconds since midnight of
* 1/1/1970 GMT.</li>
* <li>The session has two attributes. The first is "attrName" with the value of
* "someAttrValue". The second session attribute is named "attrName2" with the
* value of "someAttrValue2".</li>
* "someAttrValue". The second session attribute is named "attrName2" with the value of
* "someAttrValue2".</li>
* </ul>
*
*
* <h3>Optimized Writes</h3>
*
* <p>
* The {@link RedisSession} keeps track of the properties that have changed and
* only updates those. This means if an attribute is written once and read many
* times we only need to write that attribute once. For example, assume the
* session attribute "sessionAttr2" from earlier was updated. The following
* would be executed upon saving:
* The {@link RedisSession} keeps track of the properties that have changed and only
* updates those. This means if an attribute is written once and read many times we only
* need to write that attribute once. For example, assume the session attribute
* "sessionAttr2" from earlier was updated. The following would be executed upon saving:
* </p>
*
* <pre>
@@ -139,15 +136,15 @@ import org.springframework.util.Assert;
*
* <p>
* When a session is created an event is sent to Redis with the channel of
* "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such
* that "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the sesion id. The body of the
* event will be the session that was created.
* "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such that
* "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the sesion id. The body of the event will be
* the session that was created.
* </p>
*
* <p>
* If registered as a {@link MessageListener}, then
* {@link RedisOperationsSessionRepository} will then translate the Redis
* message into a {@link SessionCreatedEvent}.
* {@link RedisOperationsSessionRepository} will then translate the Redis message into a
* {@link SessionCreatedEvent}.
* </p>
*
* <h3>Expiration</h3>
@@ -164,33 +161,32 @@ import org.springframework.util.Assert;
* </pre>
*
* <p>
* You will note that the expiration that is set is 5 minutes after the session
* actually expires. This is necessary so that the value of the session can be
* accessed when the session expires. An expiration is set on the session itself
* five minutes after it actually expires to ensure it is cleaned up, but only
* after we perform any necessary processing.
* You will note that the expiration that is set is 5 minutes after the session actually
* expires. This is necessary so that the value of the session can be accessed when the
* session expires. An expiration is set on the session itself five minutes after it
* actually expires to ensure it is cleaned up, but only after we perform any necessary
* processing.
* </p>
*
* <p>
* <b>NOTE:</b> The {@link #getSession(String)} method ensures that no expired
* sessions will be returned. This means there is no need to check the
* expiration before using a session
* <b>NOTE:</b> The {@link #getSession(String)} method ensures that no expired sessions
* will be returned. This means there is no need to check the expiration before using a
* session
* </p>
*
* <p>
* Spring Session relies on the expired and delete
* <a href="http://redis.io/topics/notifications">keyspace notifications</a>
* from Redis to fire a SessionDestroyedEvent. It is the SessionDestroyedEvent
* that ensures resources associated with the Session are cleaned up. For
* example, when using Spring Session's WebSocket support the Redis expired or
* delete event is what triggers any WebSocket connections associated with the
* session to be closed.
* <a href="http://redis.io/topics/notifications">keyspace notifications</a> from Redis to
* fire a SessionDestroyedEvent. It is the SessionDestroyedEvent that ensures resources
* associated with the Session are cleaned up. For example, when using Spring Session's
* WebSocket support the Redis expired or delete event is what triggers any WebSocket
* connections associated with the session to be closed.
* </p>
*
* <p>
* Expiration is not tracked directly on the session key itself since this would
* mean the session data would no longer be available. Instead a special session
* expires key is used. In our example the expires key is:
* Expiration is not tracked directly on the session key itself since this would mean the
* session data would no longer be available. Instead a special session expires key is
* used. In our example the expires key is:
* </p>
*
* <pre>
@@ -199,33 +195,30 @@ import org.springframework.util.Assert;
* </pre>
*
* <p>
* When a session expires key is deleted or expires, the keyspace notification
* triggers a lookup of the actual session and a {@link SessionDestroyedEvent}
* is fired.
* When a session expires key is deleted or expires, the keyspace notification triggers a
* lookup of the actual session and a {@link SessionDestroyedEvent} is fired.
* </p>
*
* <p>
* One problem with relying on Redis expiration exclusively is that Redis makes
* no guarantee of when the expired event will be fired if they key has not been
* accessed. Specifically the background task that Redis uses to clean up
* expired keys is a low priority task and may not trigger the key expiration.
* For additional details see
* <a href="http://redis.io/topics/notifications">Timing of expired events</a>
* section in the Redis documentation.
* One problem with relying on Redis expiration exclusively is that Redis makes no
* guarantee of when the expired event will be fired if they key has not been accessed.
* Specifically the background task that Redis uses to clean up expired keys is a low
* priority task and may not trigger the key expiration. For additional details see
* <a href="http://redis.io/topics/notifications">Timing of expired events</a> section in
* the Redis documentation.
* </p>
*
* <p>
* To circumvent the fact that expired events are not guaranteed to happen we
* can ensure that each key is accessed when it is expected to expire. This
* means that if the TTL is expired on the key, Redis will remove the key and
* fire the expired event when we try to access they key.
* To circumvent the fact that expired events are not guaranteed to happen we can ensure
* that each key is accessed when it is expected to expire. This means that if the TTL is
* expired on the key, Redis will remove the key and fire the expired event when we try to
* access they key.
* </p>
*
* <p>
* For this reason, each session expiration is also tracked to the nearest
* minute. This allows a background task to access the potentially expired
* sessions to ensure that Redis expired events are fired in a more
* deterministic fashion. For example:
* For this reason, each session expiration is also tracked to the nearest minute. This
* allows a background task to access the potentially expired sessions to ensure that
* Redis expired events are fired in a more deterministic fashion. For example:
* </p>
*
* <pre>
@@ -234,24 +227,26 @@ import org.springframework.util.Assert;
* </pre>
*
* <p>
* The background task will then use these mappings to explicitly request each
* session expires key. By accessing the key, rather than deleting it, we ensure
* that Redis deletes the key for us only if the TTL is expired.
* The background task will then use these mappings to explicitly request each session
* expires key. By accessing the key, rather than deleting it, we ensure that Redis
* deletes the key for us only if the TTL is expired.
* </p>
* <p>
* <b>NOTE</b>: We do not explicitly delete the keys since in some instances
* there may be a race condition that incorrectly identifies a key as expired
* when it is not. Short of using distributed locks (which would kill our
* performance) there is no way to ensure the consistency of the expiration
* mapping. By simply accessing the key, we ensure that the key is only removed
* if the TTL on that key is expired.
* <b>NOTE</b>: We do not explicitly delete the keys since in some instances there may be
* a race condition that incorrectly identifies a key as expired when it is not. Short of
* using distributed locks (which would kill our performance) there is no way to ensure
* the consistency of the expiration mapping. By simply accessing the key, we ensure that
* the key is only removed if the TTL on that key is expired.
* </p>
*
* @author Rob Winch
* @since 1.0
*/
public class RedisOperationsSessionRepository implements FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>, MessageListener {
private static final Log logger = LogFactory.getLog(RedisOperationsSessionRepository.class);
public class RedisOperationsSessionRepository implements
FindByIndexNameSessionRepository<RedisOperationsSessionRepository.RedisSession>,
MessageListener {
private static final Log logger = LogFactory
.getLog(RedisOperationsSessionRepository.class);
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@@ -263,23 +258,28 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
static final String DEFAULT_SPRING_SESSION_REDIS_PREFIX = "spring:session:";
/**
* The key in the Hash representing {@link org.springframework.session.ExpiringSession#getCreationTime()}.
* The key in the Hash representing
* {@link org.springframework.session.ExpiringSession#getCreationTime()}.
*/
static final String CREATION_TIME_ATTR = "creationTime";
/**
* The key in the Hash representing {@link org.springframework.session.ExpiringSession#getMaxInactiveIntervalInSeconds()}.
* The key in the Hash representing
* {@link org.springframework.session.ExpiringSession#getMaxInactiveIntervalInSeconds()}
* .
*/
static final String MAX_INACTIVE_ATTR = "maxInactiveInterval";
/**
* The key in the Hash representing {@link org.springframework.session.ExpiringSession#getLastAccessedTime()}.
* The key in the Hash representing
* {@link org.springframework.session.ExpiringSession#getLastAccessedTime()}.
*/
static final String LAST_ACCESSED_ATTR = "lastAccessedTime";
/**
* The prefix of the key for used for session attributes. The suffix is the name of the session attribute. For
* example, if the session contained an attribute named attributeName, then there would be an entry in the hash named
* The prefix of the key for used for session attributes. The suffix is the name of
* the session attribute. For example, if the session contained an attribute named
* attributeName, then there would be an entry in the hash named
* sessionAttr:attributeName that mapped to its value.
*/
static final String SESSION_ATTR_PREFIX = "sessionAttr:";
@@ -296,12 +296,14 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() {
public void publishEvent(ApplicationEvent event) {
}
public void publishEvent(Object event) {
}
};
/**
* If non-null, this value is used to override the default value for {@link RedisSession#setMaxInactiveIntervalInSeconds(int)}.
* If non-null, this value is used to override the default value for
* {@link RedisSession#setMaxInactiveIntervalInSeconds(int)}.
*/
private Integer defaultMaxInactiveInterval;
@@ -310,23 +312,28 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
private RedisFlushMode redisFlushMode = RedisFlushMode.ON_SAVE;
/**
* Allows creating an instance and uses a default {@link RedisOperations} for both managing the session and the expirations.
* Allows creating an instance and uses a default {@link RedisOperations} for both
* managing the session and the expirations.
*
* @param redisConnectionFactory the {@link RedisConnectionFactory} to use.
*/
public RedisOperationsSessionRepository(RedisConnectionFactory redisConnectionFactory) {
public RedisOperationsSessionRepository(
RedisConnectionFactory redisConnectionFactory) {
this(createDefaultTemplate(redisConnectionFactory));
}
/**
* Creates a new instance. For an example, refer to the class level javadoc.
*
* @param sessionRedisOperations The {@link RedisOperations} to use for managing the sessions. Cannot be null.
* @param sessionRedisOperations The {@link RedisOperations} to use for managing the
* sessions. Cannot be null.
*/
public RedisOperationsSessionRepository(RedisOperations<Object, Object> sessionRedisOperations) {
public RedisOperationsSessionRepository(
RedisOperations<Object, Object> sessionRedisOperations) {
Assert.notNull(sessionRedisOperations, "sessionRedisOperations cannot be null");
this.sessionRedisOperations = sessionRedisOperations;
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations, this);
this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations,
this);
}
/**
@@ -334,21 +341,23 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
* {@link SessionDestroyedEvent}. The default is to not publish a
* {@link SessionDestroyedEvent}.
*
* @param applicationEventPublisher
* the {@link ApplicationEventPublisher} that is used to publish
* {@link SessionDestroyedEvent}. Cannot be null.
* @param applicationEventPublisher the {@link ApplicationEventPublisher} that is used
* to publish {@link SessionDestroyedEvent}. Cannot be null.
*/
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher, "applicationEventPublisher cannot be null");
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
Assert.notNull(applicationEventPublisher,
"applicationEventPublisher cannot be null");
this.eventPublisher = applicationEventPublisher;
}
/**
* Sets the maximum inactive interval in seconds between requests before newly created sessions will be
* invalidated. A negative time indicates that the session will never timeout. The default is 1800 (30 minutes).
* Sets the maximum inactive interval in seconds between requests before newly created
* sessions will be invalidated. A negative time indicates that the session will never
* timeout. The default is 1800 (30 minutes).
*
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between
* client requests.
* @param defaultMaxInactiveInterval the number of seconds that the {@link Session}
* should be kept alive between client requests.
*/
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
@@ -393,13 +402,16 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
return getSession(id, false);
}
public Map<String, RedisSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
public Map<String, RedisSession> findByIndexNameAndIndexValue(String indexName,
String indexValue) {
if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
return Collections.emptyMap();
}
String principalKey = getPrincipalKey(indexValue);
Set<Object> sessionIds = this.sessionRedisOperations.boundSetOps(principalKey).members();
Map<String, RedisSession> sessions = new HashMap<String, RedisSession>(sessionIds.size());
Set<Object> sessionIds = this.sessionRedisOperations.boundSetOps(principalKey)
.members();
Map<String, RedisSession> sessions = new HashMap<String, RedisSession>(
sessionIds.size());
for (Object id : sessionIds) {
RedisSession session = getSession((String) id);
if (session != null) {
@@ -412,10 +424,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
/**
* Gets the session.
* @param id the session id
* @param allowExpired
* if true, will also include expired sessions that have not been
* deleted. If false, will ensure expired sessions are not
* returned.
* @param allowExpired if true, will also include expired sessions that have not been
* deleted. If false, will ensure expired sessions are not returned.
* @return the Redis session
*/
private RedisSession getSession(String id, boolean allowExpired) {
@@ -446,7 +456,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
loaded.setLastAccessedTime((Long) entry.getValue());
}
else if (key.startsWith(SESSION_ATTR_PREFIX)) {
loaded.setAttribute(key.substring(SESSION_ATTR_PREFIX.length()), entry.getValue());
loaded.setAttribute(key.substring(SESSION_ATTR_PREFIX.length()),
entry.getValue());
}
}
return loaded;
@@ -486,10 +497,10 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
String channel = new String(messageChannel);
if (channel.startsWith(getSessionCreatedChannelPrefix())) {
// TODO: is this thread safe?
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer.deserialize(message.getBody());
Map<Object, Object> loaded = (Map<Object, Object>) this.defaultSerializer
.deserialize(message.getBody());
handleCreated(loaded, channel);
return;
}
@@ -531,7 +542,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
String sessionId = session.getId();
String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(session);
if (principal != null) {
this.sessionRedisOperations.boundSetOps(getPrincipalKey(principal)).remove(sessionId);
this.sessionRedisOperations.boundSetOps(getPrincipalKey(principal))
.remove(sessionId);
}
}
@@ -583,7 +595,9 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
}
String getPrincipalKey(String principalName) {
return this.keyPrefix + "index:" + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":" + principalName;
return this.keyPrefix + "index:"
+ FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME + ":"
+ principalName;
}
String getExpirationsKey(long expiration) {
@@ -603,7 +617,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
}
/**
* Gets the prefix for the channel that SessionCreatedEvent are published to. The suffix is the session id of the session that was created.
* Gets the prefix for the channel that SessionCreatedEvent are published to. The
* suffix is the session id of the session that was created.
*
* @return the prefix for the channel that SessionCreatedEvent are published to
*/
@@ -616,7 +631,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
* @param sessionId the id of the {@link Session} to work with
* @return the {@link BoundHashOperations} to operate on a {@link Session}
*/
private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(String sessionId) {
private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(
String sessionId) {
String key = getSessionKey(sessionId);
return this.sessionRedisOperations.boundHashOps(key);
}
@@ -631,7 +647,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
return SESSION_ATTR_PREFIX + attributeName;
}
private static RedisTemplate<Object, Object> createDefaultTemplate(RedisConnectionFactory connectionFactory) {
private static RedisTemplate<Object, Object> createDefaultTemplate(
RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "connectionFactory cannot be null");
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setKeySerializer(new StringRedisSerializer());
@@ -642,10 +659,10 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
}
/**
* A custom implementation of {@link Session} that uses a {@link MapSession} as the basis for its mapping. It keeps
* track of any attributes that have changed. When
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()} is invoked
* all the attributes that have been changed will be persisted.
* A custom implementation of {@link Session} that uses a {@link MapSession} as the
* basis for its mapping. It keeps track of any attributes that have changed. When
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()}
* is invoked all the attributes that have been changed will be persisted.
*
* @author Rob Winch
* @since 1.0
@@ -658,7 +675,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
private String originalPrincipalName;
/**
* Creates a new instance ensuring to mark all of the new attributes to be persisted in the next save operation.
* Creates a new instance ensuring to mark all of the new attributes to be
* persisted in the next save operation.
*/
RedisSession() {
this(new MapSession());
@@ -672,7 +690,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
/**
* Creates a new instance from the provided {@link MapSession}.
*
* @param cached the {@MapSession} that represents the persisted session that was retrieved. Cannot be null.
* @param cached the {@MapSession} that represents the persisted session that was
* retrieved. Cannot be null.
*/
RedisSession(MapSession cached) {
Assert.notNull("MapSession cannot be null");
@@ -748,30 +767,40 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
}
/**
* Saves any attributes that have been changed and updates the expiration of this session.
* Saves any attributes that have been changed and updates the expiration of this
* session.
*/
private void saveDelta() {
String sessionId = getId();
getSessionBoundHashOperations(sessionId).putAll(this.delta);
String principalSessionKey = getSessionAttrNameKey(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
String securityPrincipalSessionKey = getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);
if (this.delta.containsKey(principalSessionKey) || this.delta.containsKey(securityPrincipalSessionKey)) {
String principalSessionKey = getSessionAttrNameKey(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
String securityPrincipalSessionKey = getSessionAttrNameKey(
SPRING_SECURITY_CONTEXT);
if (this.delta.containsKey(principalSessionKey)
|| this.delta.containsKey(securityPrincipalSessionKey)) {
if (this.originalPrincipalName != null) {
String originalPrincipalRedisKey = getPrincipalKey((String) this.originalPrincipalName);
RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(originalPrincipalRedisKey).remove(sessionId);
String originalPrincipalRedisKey = getPrincipalKey(
(String) this.originalPrincipalName);
RedisOperationsSessionRepository.this.sessionRedisOperations
.boundSetOps(originalPrincipalRedisKey).remove(sessionId);
}
String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
this.originalPrincipalName = principal;
if (principal != null) {
String principalRedisKey = getPrincipalKey(principal);
RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(principalRedisKey).add(sessionId);
RedisOperationsSessionRepository.this.sessionRedisOperations
.boundSetOps(principalRedisKey).add(sessionId);
}
}
this.delta = new HashMap<String, Object>(this.delta.size());
Long originalExpiration = this.originalLastAccessTime == null ? null : this.originalLastAccessTime + TimeUnit.SECONDS.toMillis(getMaxInactiveIntervalInSeconds());
RedisOperationsSessionRepository.this.expirationPolicy.onExpirationUpdated(originalExpiration, this);
Long originalExpiration = this.originalLastAccessTime == null ? null
: this.originalLastAccessTime + TimeUnit.SECONDS
.toMillis(getMaxInactiveIntervalInSeconds());
RedisOperationsSessionRepository.this.expirationPolicy
.onExpirationUpdated(originalExpiration, this);
}
}
@@ -788,7 +817,8 @@ public class RedisOperationsSessionRepository implements FindByIndexNameSessionR
}
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = this.parser.parseExpression("authentication?.name");
Expression expression = this.parser
.parseExpression("authentication?.name");
return expression.getValue(authentication, String.class);
}
return null;

View File

@@ -30,33 +30,32 @@ import org.springframework.session.ExpiringSession;
import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession;
/**
* A strategy for expiring {@link RedisSession} instances. This performs two
* operations:
* A strategy for expiring {@link RedisSession} instances. This performs two operations:
*
* Redis has no guarantees of when an expired session event will be fired. In
* order to ensure expired session events are processed in a timely fashion the
* expiration (rounded to the nearest minute) is mapped to all the sessions that
* expire at that time. Whenever {@link #cleanExpiredSessions()} is invoked, the
* sessions for the previous minute are then accessed to ensure they are deleted if expired.
* Redis has no guarantees of when an expired session event will be fired. In order to
* ensure expired session events are processed in a timely fashion the expiration (rounded
* to the nearest minute) is mapped to all the sessions that expire at that time. Whenever
* {@link #cleanExpiredSessions()} is invoked, the sessions for the previous minute are
* then accessed to ensure they are deleted if expired.
*
* In some instances the {@link #cleanExpiredSessions()} method may not be not
* invoked for a specific time. For example, this may happen when a server is
* restarted. To account for this, the expiration on the Redis session is also set.
* In some instances the {@link #cleanExpiredSessions()} method may not be not invoked for
* a specific time. For example, this may happen when a server is restarted. To account
* for this, the expiration on the Redis session is also set.
*
* @author Rob Winch
* @since 1.0
*/
final class RedisSessionExpirationPolicy {
private static final Log logger = LogFactory.getLog(RedisSessionExpirationPolicy.class);
private static final Log logger = LogFactory
.getLog(RedisSessionExpirationPolicy.class);
private final RedisOperations<Object, Object> redis;
private final RedisOperationsSessionRepository redisSession;
RedisSessionExpirationPolicy(
RedisOperations<Object, Object> sessionRedisOperations, RedisOperationsSessionRepository redisSession) {
RedisSessionExpirationPolicy(RedisOperations<Object, Object> sessionRedisOperations,
RedisOperationsSessionRepository redisSession) {
super();
this.redis = sessionRedisOperations;
this.redisSession = redisSession;
@@ -68,7 +67,8 @@ final class RedisSessionExpirationPolicy {
this.redis.boundSetOps(expireKey).remove(session.getId());
}
public void onExpirationUpdated(Long originalExpirationTimeInMilli, ExpiringSession session) {
public void onExpirationUpdated(Long originalExpirationTimeInMilli,
ExpiringSession session) {
String keyToExpire = "expires:" + session.getId();
long toExpire = roundUpToNextMinute(expiresInMillis(session));
@@ -81,11 +81,13 @@ final class RedisSessionExpirationPolicy {
}
String expireKey = getExpirationKey(toExpire);
BoundSetOperations<Object, Object> expireOperations = this.redis.boundSetOps(expireKey);
BoundSetOperations<Object, Object> expireOperations = this.redis
.boundSetOps(expireKey);
expireOperations.add(keyToExpire);
long sessionExpireInSeconds = session.getMaxInactiveIntervalInSeconds();
long fiveMinutesAfterExpires = sessionExpireInSeconds + TimeUnit.MINUTES.toSeconds(5);
long fiveMinutesAfterExpires = sessionExpireInSeconds
+ TimeUnit.MINUTES.toSeconds(5);
String sessionKey = getSessionKey(keyToExpire);
expireOperations.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
@@ -94,9 +96,11 @@ final class RedisSessionExpirationPolicy {
}
else {
this.redis.boundValueOps(sessionKey).append("");
this.redis.boundValueOps(sessionKey).expire(sessionExpireInSeconds, TimeUnit.SECONDS);
this.redis.boundValueOps(sessionKey).expire(sessionExpireInSeconds,
TimeUnit.SECONDS);
}
this.redis.boundHashOps(getSessionKey(session.getId())).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
this.redis.boundHashOps(getSessionKey(session.getId()))
.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
}
String getExpirationKey(long expires) {
@@ -125,7 +129,8 @@ final class RedisSessionExpirationPolicy {
}
/**
* By trying to access the session we only trigger a deletion if it the TTL is expired. This is done to handle
* By trying to access the session we only trigger a deletion if it the TTL is
* expired. This is done to handle
* https://github.com/spring-projects/spring-session/issues/93
*
* @param key the key

View File

@@ -28,9 +28,9 @@ import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.util.Assert;
/**
* Listen for Redis {@link Message} notifications. If it is a "del"
* translate into a {@link SessionDeletedEvent}. If it is an "expired"
* translate into a {@link SessionExpiredEvent}.
* Listen for Redis {@link Message} notifications. If it is a "del" translate into a
* {@link SessionDeletedEvent}. If it is an "expired" translate into a
* {@link SessionExpiredEvent}.
*
* @author Rob Winch
* @author Mark Anderson

View File

@@ -44,8 +44,12 @@ public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction
static final String CONFIG_NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events";
/* (non-Javadoc)
* @see org.springframework.session.data.redis.config.ConfigureRedisAction#configure(org.springframework.data.redis.connection.RedisConnection)
/*
* (non-Javadoc)
*
* @see
* org.springframework.session.data.redis.config.ConfigureRedisAction#configure(org.
* springframework.data.redis.connection.RedisConnection)
*/
public void configure(RedisConnection connection) {
String notifyOptions = getNotifyOptions(connection);
@@ -74,7 +78,9 @@ public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction
return config.get(1);
}
catch (InvalidDataAccessApiUsageException e) {
throw new IllegalStateException("Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent", e);
throw new IllegalStateException(
"Unable to configure Redis to keyspace notifications. See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent",
e);
}
}

View File

@@ -29,10 +29,9 @@ import org.springframework.session.data.redis.RedisFlushMode;
/**
* Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and
* backed by Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory}
* must be provided. For example:
* <pre>
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
* Redis. In order to leverage the annotation, a single {@link RedisConnectionFactory}
* must be provided. For example: <pre>
* <code>
* {@literal @Configuration}
* {@literal @EnableRedisHttpSession}
@@ -44,8 +43,7 @@ import org.springframework.session.data.redis.RedisFlushMode;
* }
*
* }
* </code>
* </pre>
* </code> </pre>
*
* More advanced configurations can extend {@link RedisHttpSessionConfiguration} instead.
*
@@ -54,7 +52,7 @@ import org.springframework.session.data.redis.RedisFlushMode;
* @see EnableSpringHttpSession
*/
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.TYPE})
@Target({ java.lang.annotation.ElementType.TYPE })
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
@@ -63,17 +61,16 @@ public @interface EnableRedisHttpSession {
/**
* <p>
* Defines a unique namespace for keys. The value is used to isolate
* sessions by changing the prefix from "spring:session:" to
* Defines a unique namespace for keys. The value is used to isolate sessions by
* changing the prefix from "spring:session:" to
* "spring:session:&lt;redisNamespace&gt;:". The default is "" such that all Redis
* keys begin with "spring:session".
* </p>
*
* <p>
* For example, if you had an application named "Application A" that needed
* to keep the sessions isolated from "Application B" you could set two
* different values for the applications and they could function within the
* same Redis instance.
* For example, if you had an application named "Application A" that needed to keep
* the sessions isolated from "Application B" you could set two different values for
* the applications and they could function within the same Redis instance.
* </p>
*
* @return the unique namespace for keys
@@ -82,15 +79,14 @@ public @interface EnableRedisHttpSession {
/**
* <p>
* Sets the flush mode for the Redis sessions. The default is IMMEDIATE
* which only updates the backing Redis when
* {@link SessionRepository#save(org.springframework.session.Session)} is
* invoked. In a web environment this happens just before the HTTP resposne
* is committed.
* Sets the flush mode for the Redis sessions. The default is IMMEDIATE which only
* updates the backing Redis when
* {@link SessionRepository#save(org.springframework.session.Session)} is invoked. In
* a web environment this happens just before the HTTP resposne is committed.
* </p>
* <p>
* Setting the value to IMMEDIATE will ensure that the any updates to the
* Session are immediately written to the Redis instance.
* Setting the value to IMMEDIATE will ensure that the any updates to the Session are
* immediately written to the Redis instance.
* </p>
*
* @return the {@link RedisFlushMode} to use

View File

@@ -59,7 +59,8 @@ import org.springframework.util.StringUtils;
*/
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
private Integer maxInactiveIntervalInSeconds = 1800;
@@ -77,7 +78,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory, RedisOperationsSessionRepository messageListener) {
RedisConnectionFactory connectionFactory,
RedisOperationsSessionRepository messageListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
@@ -88,13 +90,16 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
}
container.addMessageListener(messageListener,
Arrays.asList(new PatternTopic("__keyevent@*:del"), new PatternTopic("__keyevent@*:expired")));
container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(messageListener.getSessionCreatedChannelPrefix() + "*")));
Arrays.asList(new PatternTopic("__keyevent@*:del"),
new PatternTopic("__keyevent@*:expired")));
container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(
messageListener.getSessionCreatedChannelPrefix() + "*")));
return container;
}
@Bean
public RedisTemplate<Object, Object> sessionRedisTemplate(RedisConnectionFactory connectionFactory) {
public RedisTemplate<Object, Object> sessionRedisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
@@ -106,10 +111,14 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
}
@Bean
public RedisOperationsSessionRepository sessionRepository(@Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate, ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(sessionRedisTemplate);
public RedisOperationsSessionRepository sessionRepository(
@Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate,
ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
sessionRedisTemplate);
sessionRepository.setApplicationEventPublisher(applicationEventPublisher);
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
}
@@ -145,22 +154,27 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
Map<String, Object> enableAttrMap = importMetadata
.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
this.maxInactiveIntervalInSeconds = enableAttrs.getNumber("maxInactiveIntervalInSeconds");
this.maxInactiveIntervalInSeconds = enableAttrs
.getNumber("maxInactiveIntervalInSeconds");
this.redisNamespace = enableAttrs.getString("redisNamespace");
this.redisFlushMode = enableAttrs.getEnum("redisFlushMode");
}
@Bean
public InitializingBean enableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory) {
return new EnableRedisKeyspaceNotificationsInitializer(connectionFactory, this.configureRedisAction);
public InitializingBean enableRedisKeyspaceNotificationsInitializer(
RedisConnectionFactory connectionFactory) {
return new EnableRedisKeyspaceNotificationsInitializer(connectionFactory,
this.configureRedisAction);
}
/**
* Sets the action to perform for configuring Redis.
*
* @param configureRedisAction the configureRedis to set. The default is {@link ConfigureNotifyKeyspaceEventsAction}.
* @param configureRedisAction the configureRedis to set. The default is
* {@link ConfigureNotifyKeyspaceEventsAction}.
*/
@Autowired(required = false)
public void setConfigureRedisAction(ConfigureRedisAction configureRedisAction) {
@@ -169,7 +183,8 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
@Autowired(required = false)
@Qualifier("springSessionDefaultRedisSerializer")
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
public void setDefaultRedisSerializer(
RedisSerializer<Object> defaultRedisSerializer) {
this.defaultRedisSerializer = defaultRedisSerializer;
}
@@ -186,16 +201,20 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
}
/**
* Ensures that Redis is configured to send keyspace notifications. This is important to ensure that expiration and
* deletion of sessions trigger SessionDestroyedEvents. Without the SessionDestroyedEvent resources may not get
* cleaned up properly. For example, the mapping of the Session to WebSocket connections may not get cleaned up.
* Ensures that Redis is configured to send keyspace notifications. This is important
* to ensure that expiration and deletion of sessions trigger SessionDestroyedEvents.
* Without the SessionDestroyedEvent resources may not get cleaned up properly. For
* example, the mapping of the Session to WebSocket connections may not get cleaned
* up.
*/
static class EnableRedisKeyspaceNotificationsInitializer implements InitializingBean {
private final RedisConnectionFactory connectionFactory;
private ConfigureRedisAction configure;
EnableRedisKeyspaceNotificationsInitializer(RedisConnectionFactory connectionFactory, ConfigureRedisAction configure) {
EnableRedisKeyspaceNotificationsInitializer(
RedisConnectionFactory connectionFactory,
ConfigureRedisAction configure) {
this.connectionFactory = connectionFactory;
this.configure = configure;
}

View File

@@ -21,8 +21,8 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* For {@link SessionRepository} implementations that support it, this event is
* fired when a {@link Session} is updated.
* For {@link SessionRepository} implementations that support it, this event is fired when
* a {@link Session} is updated.
*
* @author Rob Winch
* @since 1.1
@@ -46,12 +46,13 @@ public abstract class AbstractSessionEvent extends ApplicationEvent {
}
/**
* Gets the {@link Session} that was destroyed. For some
* {@link SessionRepository} implementations it may not be possible to get
* the original session in which case this may be null.
* Gets the {@link Session} that was destroyed. For some {@link SessionRepository}
* implementations it may not be possible to get the original session in which case
* this may be null.
*
* @param <S> The type of Session
* @return the expired {@link Session} or null if the data store does not support obtaining it
* @return the expired {@link Session} or null if the data store does not support
* obtaining it
*/
@SuppressWarnings("unchecked")
public <S extends Session> S getSession() {

View File

@@ -20,9 +20,8 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* For {@link SessionRepository} implementations that support it, this event is
* fired when a {@link Session} is destroyed either explicitly or via
* expiration.
* For {@link SessionRepository} implementations that support it, this event is fired when
* a {@link Session} is destroyed either explicitly or via expiration.
*
* @author Rob Winch
* @since 1.0

View File

@@ -20,8 +20,8 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* For {@link SessionRepository} implementations that support it, this event is
* fired when a {@link Session} is destroyed via deletion.
* For {@link SessionRepository} implementations that support it, this event is fired when
* a {@link Session} is destroyed via deletion.
*
* @author Mark Anderson
* @author Rob Winch

View File

@@ -20,8 +20,8 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* For {@link SessionRepository} implementations that support it, this event is
* fired when a {@link Session} is destroyed via expiration.
* For {@link SessionRepository} implementations that support it, this event is fired when
* a {@link Session} is destroyed via expiration.
*
* @author Mark Anderson
* @author Rob Winch

View File

@@ -31,13 +31,13 @@ import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.util.Assert;
/**
* Listen for events on the Hazelcast-backed SessionRepository and
* translate those events into the corresponding Spring Session events.
* Publish the Spring Session events with the given {@link ApplicationEventPublisher}.
* Listen for events on the Hazelcast-backed SessionRepository and translate those events
* into the corresponding Spring Session events. Publish the Spring Session events with
* the given {@link ApplicationEventPublisher}.
* <ul>
* <li>entryAdded - {@link SessionCreatedEvent}</li>
* <li>entryEvicted - {@link SessionExpiredEvent}</li>
* <li>entryRemoved - {@link SessionDeletedEvent}</li>
* <li>entryAdded - {@link SessionCreatedEvent}</li>
* <li>entryEvicted - {@link SessionExpiredEvent}</li>
* <li>entryRemoved - {@link SessionDeletedEvent}</li>
* </ul>
*
* @author Tommy Ludwig
@@ -45,7 +45,8 @@ import org.springframework.util.Assert;
* @since 1.1
*/
public class SessionEntryListener implements EntryAddedListener<String, ExpiringSession>,
EntryEvictedListener<String, ExpiringSession>, EntryRemovedListener<String, ExpiringSession> {
EntryEvictedListener<String, ExpiringSession>,
EntryRemovedListener<String, ExpiringSession> {
private static final Log logger = LogFactory.getLog(SessionEntryListener.class);
private ApplicationEventPublisher eventPublisher;
@@ -66,14 +67,16 @@ public class SessionEntryListener implements EntryAddedListener<String, Expiring
if (logger.isDebugEnabled()) {
logger.debug("Session expired with id: " + event.getOldValue().getId());
}
this.eventPublisher.publishEvent(new SessionExpiredEvent(this, event.getOldValue()));
this.eventPublisher
.publishEvent(new SessionExpiredEvent(this, event.getOldValue()));
}
public void entryRemoved(EntryEvent<String, ExpiringSession> event) {
if (logger.isDebugEnabled()) {
logger.debug("Session deleted with id: " + event.getOldValue().getId());
}
this.eventPublisher.publishEvent(new SessionDeletedEvent(this, event.getOldValue()));
this.eventPublisher
.publishEvent(new SessionDeletedEvent(this, event.getOldValue()));
}
}

View File

@@ -26,10 +26,9 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
/**
* Add this annotation to a {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and
* backed by Hazelcast. In order to leverage the annotation, a single HazelcastInstance
* must be provided. For example:
* <pre>
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by
* Hazelcast. In order to leverage the annotation, a single HazelcastInstance must be
* provided. For example: <pre>
* <code>
* {@literal @Configuration}
* {@literal @EnableHazelcastHttpSession}
@@ -42,32 +41,32 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
* }
*
* }
* </code>
* </pre>
* </code> </pre>
*
* More advanced configurations can extend {@link HazelcastHttpSessionConfiguration} instead.
* More advanced configurations can extend {@link HazelcastHttpSessionConfiguration}
* instead.
*
* @author Tommy Ludwig
* @since 1.1
* @see EnableSpringHttpSession
*/
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.TYPE})
@Target({ java.lang.annotation.ElementType.TYPE })
@Documented
@Import(HazelcastHttpSessionConfiguration.class)
@Configuration
public @interface EnableHazelcastHttpSession {
/**
* This is the session timeout in seconds. By default, it is set to 1800 seconds (30 minutes).
* This should be a non-negative integer.
* This is the session timeout in seconds. By default, it is set to 1800 seconds (30
* minutes). This should be a non-negative integer.
*
* @return the seconds a session can be inactive before expiring
*/
int maxInactiveIntervalInSeconds() default 1800;
/**
* This is the name of the Map that will be used in Hazelcast to store the session data.
* Default is "spring:session:sessions".
* This is the name of the Map that will be used in Hazelcast to store the session
* data. Default is "spring:session:sessions".
* @return the name of the Map to store the sessions in Hazelcast
*/
String sessionMapName() default "spring:session:sessions";

View File

@@ -49,7 +49,8 @@ import org.springframework.session.web.http.SessionRepositoryFilter;
* @see EnableHazelcastHttpSession
*/
@Configuration
public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
private Integer maxInactiveIntervalInSeconds = 1800;
@@ -60,12 +61,16 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
private IMap<String, ExpiringSession> sessionsMap;
@Bean
public SessionRepository<ExpiringSession> sessionRepository(HazelcastInstance hazelcastInstance, SessionEntryListener sessionListener) {
public SessionRepository<ExpiringSession> sessionRepository(
HazelcastInstance hazelcastInstance, SessionEntryListener sessionListener) {
this.sessionsMap = hazelcastInstance.getMap(this.sessionMapName);
this.sessionListenerUid = this.sessionsMap.addEntryListener(sessionListener, true);
this.sessionListenerUid = this.sessionsMap.addEntryListener(sessionListener,
true);
MapSessionRepository sessionRepository = new MapSessionRepository(new ExpiringSessionMap(this.sessionsMap));
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
MapSessionRepository sessionRepository = new MapSessionRepository(
new ExpiringSessionMap(this.sessionsMap));
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
return sessionRepository;
}
@@ -76,19 +81,22 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
}
@Bean
public SessionEntryListener sessionListener(ApplicationEventPublisher eventPublisher) {
public SessionEntryListener sessionListener(
ApplicationEventPublisher eventPublisher) {
return new SessionEntryListener(eventPublisher);
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
Map<String, Object> enableAttrMap = importMetadata
.getAnnotationAttributes(EnableHazelcastHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
transferAnnotationAttributes(enableAttrs);
}
private void transferAnnotationAttributes(AnnotationAttributes enableAttrs) {
setMaxInactiveIntervalInSeconds((Integer) enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
setMaxInactiveIntervalInSeconds(
(Integer) enableAttrs.getNumber("maxInactiveIntervalInSeconds"));
setSessionMapName(enableAttrs.getString("sessionMapName"));
}
@@ -109,11 +117,13 @@ public class HazelcastHttpSessionConfiguration extends SpringHttpSessionConfigur
ExpiringSessionMap(IMap<String, ExpiringSession> delegate) {
this.delegate = delegate;
}
public ExpiringSession put(String key, ExpiringSession value) {
if (value == null) {
return this.delegate.put(key, value);
}
return this.delegate.put(key, value, value.getMaxInactiveIntervalInSeconds(), TimeUnit.SECONDS);
return this.delegate.put(key, value, value.getMaxInactiveIntervalInSeconds(),
TimeUnit.SECONDS);
}
public int size() {

View File

@@ -31,6 +31,7 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.GenericConversionService;
@@ -95,33 +96,27 @@ import org.springframework.util.StringUtils;
* @author Vedran Pavic
* @since 1.2.0
*/
public class JdbcOperationsSessionRepository
implements FindByIndexNameSessionRepository<JdbcOperationsSessionRepository.JdbcSession> {
public class JdbcOperationsSessionRepository implements
FindByIndexNameSessionRepository<JdbcOperationsSessionRepository.JdbcSession> {
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 CREATE_SESSION_QUERY = "INSERT INTO %TABLE_NAME%(SESSION_ID, LAST_ACCESS_TIME, PRINCIPAL_NAME, SESSION_BYTES) VALUES (?, ?, ?, ?)";
private static final String GET_SESSION_QUERY =
"SELECT SESSION_BYTES FROM %TABLE_NAME% WHERE SESSION_ID = ?";
private static final String GET_SESSION_QUERY = "SELECT SESSION_BYTES FROM %TABLE_NAME% WHERE SESSION_ID = ?";
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 UPDATE_SESSION_QUERY = "UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ?, PRINCIPAL_NAME = ?, SESSION_BYTES = ? WHERE SESSION_ID = ?";
private static final String UPDATE_SESSION_LAST_ACCESS_TIME_QUERY =
"UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ? WHERE SESSION_ID = ?";
private static final String UPDATE_SESSION_LAST_ACCESS_TIME_QUERY = "UPDATE %TABLE_NAME% SET LAST_ACCESS_TIME = ? WHERE SESSION_ID = ?";
private static final String DELETE_SESSION_QUERY =
"DELETE FROM %TABLE_NAME% WHERE SESSION_ID = ?";
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 SESSION_BYTES FROM %TABLE_NAME% WHERE PRINCIPAL_NAME = ?";
private static final String LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY = "SELECT SESSION_BYTES FROM %TABLE_NAME% WHERE PRINCIPAL_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_SESSIONS_BY_LAST_ACCESS_TIME_QUERY = "DELETE FROM %TABLE_NAME% WHERE LAST_ACCESS_TIME < ?";
private static final Log logger = LogFactory.getLog(JdbcOperationsSessionRepository.class);
private static final Log logger = LogFactory
.getLog(JdbcOperationsSessionRepository.class);
private static final PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver();
@@ -190,6 +185,8 @@ public class JdbcOperationsSessionRepository
}
/**
* Sets the {@link ConversionService} to use.
*
* @param conversionService the converter to set
*/
public void setConversionService(ConversionService conversionService) {
@@ -207,41 +204,49 @@ public class JdbcOperationsSessionRepository
public void save(final JdbcSession session) {
if (session.isNew()) {
this.jdbcOperations.update(getQuery(CREATE_SESSION_QUERY), new PreparedStatementSetter() {
this.jdbcOperations.update(getQuery(CREATE_SESSION_QUERY),
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime());
ps.setString(3, session.getPrincipalName());
JdbcOperationsSessionRepository.this.lobHandler.getLobCreator()
.setBlobAsBytes(ps, 4, serialize(session.delegate));
}
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime());
ps.setString(3, session.getPrincipalName());
JdbcOperationsSessionRepository.this.lobHandler
.getLobCreator()
.setBlobAsBytes(ps, 4, serialize(session.delegate));
}
});
});
}
else {
if (session.isAttributesChanged()) {
this.jdbcOperations.update(getQuery(UPDATE_SESSION_QUERY), new PreparedStatementSetter() {
this.jdbcOperations.update(getQuery(UPDATE_SESSION_QUERY),
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setLong(1, session.getLastAccessedTime());
ps.setString(2, session.getPrincipalName());
JdbcOperationsSessionRepository.this.lobHandler.getLobCreator()
.setBlobAsBytes(ps, 3, serialize(session.delegate));
ps.setString(4, session.getId());
}
public void setValues(PreparedStatement ps)
throws SQLException {
ps.setLong(1, session.getLastAccessedTime());
ps.setString(2, session.getPrincipalName());
JdbcOperationsSessionRepository.this.lobHandler
.getLobCreator().setBlobAsBytes(ps, 3,
serialize(session.delegate));
ps.setString(4, session.getId());
}
});
});
}
else if (session.isLastAccessTimeChanged()) {
this.jdbcOperations.update(getQuery(UPDATE_SESSION_LAST_ACCESS_TIME_QUERY), new PreparedStatementSetter() {
this.jdbcOperations.update(
getQuery(UPDATE_SESSION_LAST_ACCESS_TIME_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 {
ps.setLong(1, session.getLastAccessedTime());
ps.setString(2, session.getId());
}
});
});
}
else {
return;
@@ -274,15 +279,18 @@ public class JdbcOperationsSessionRepository
this.jdbcOperations.update(getQuery(DELETE_SESSION_QUERY), id);
}
public Map<String, JdbcSession> findByIndexNameAndIndexValue(String indexName, String indexValue) {
public Map<String, JdbcSession> findByIndexNameAndIndexValue(String indexName,
String indexValue) {
if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) {
return Collections.emptyMap();
}
List<ExpiringSession> sessions = this.jdbcOperations.query(
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY), new Object[] { indexValue }, this.mapper);
getQuery(LIST_SESSIONS_BY_PRINCIPAL_NAME_QUERY),
new Object[] { indexValue }, this.mapper);
Map<String, JdbcSession> sessionMap = new HashMap<String, JdbcSession>(sessions.size());
Map<String, JdbcSession> sessionMap = new HashMap<String, JdbcSession>(
sessions.size());
for (ExpiringSession session : sessions) {
sessionMap.put(session.getId(), new JdbcSession(session));
@@ -300,8 +308,8 @@ public class JdbcOperationsSessionRepository
logger.debug("Cleaning up sessions expiring at " + new Date(roundedNow));
}
int deletedCount = this.jdbcOperations.update(
getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY), roundedNow);
int deletedCount = this.jdbcOperations
.update(getQuery(DELETE_SESSIONS_BY_LAST_ACCESS_TIME_QUERY), roundedNow);
if (logger.isDebugEnabled()) {
logger.debug("Cleaned up " + deletedCount + " expired sessions");
@@ -319,7 +327,9 @@ public class JdbcOperationsSessionRepository
}
private byte[] serialize(ExpiringSession session) {
return (byte[]) this.conversionService.convert(session, TypeDescriptor.valueOf(ExpiringSession.class), TypeDescriptor.valueOf(byte[].class));
return (byte[]) this.conversionService.convert(session,
TypeDescriptor.valueOf(ExpiringSession.class),
TypeDescriptor.valueOf(byte[].class));
}
private static long roundDownMinute(long timeInMs) {
@@ -332,12 +342,18 @@ public class JdbcOperationsSessionRepository
private static GenericConversionService createDefaultConversionService() {
GenericConversionService converter = new GenericConversionService();
converter.addConverter(ExpiringSession.class, byte[].class, new SerializingConverter());
converter.addConverter(byte[].class, ExpiringSession.class, new DeserializingConverter());
converter.addConverter(ExpiringSession.class, byte[].class,
new SerializingConverter());
converter.addConverter(byte[].class, ExpiringSession.class,
new DeserializingConverter());
return converter;
}
/**
* The {@link ExpiringSession} to use for {@link JdbcOperationsSessionRepository}.
*
* @author Vedran Pavic
*/
final class JdbcSession implements ExpiringSession {
private final ExpiringSession delegate;
@@ -348,12 +364,12 @@ public class JdbcOperationsSessionRepository
private boolean attributesChanged;
public JdbcSession() {
JdbcSession() {
this.delegate = new MapSession();
this.isNew = true;
}
public JdbcSession(ExpiringSession delegate) {
JdbcSession(ExpiringSession delegate) {
Assert.notNull("ExpiringSession cannot be null");
this.delegate = delegate;
}
@@ -430,6 +446,11 @@ public class JdbcOperationsSessionRepository
}
/**
* Resolves the Spring Security principal name.
*
* @author Vedran Pavic
*/
static class PrincipalNameResolver {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@@ -443,7 +464,8 @@ public class JdbcOperationsSessionRepository
}
Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT);
if (authentication != null) {
Expression expression = this.parser.parseExpression("authentication?.name");
Expression expression = this.parser
.parseExpression("authentication?.name");
return expression.getValue(authentication, String.class);
}
return null;
@@ -454,8 +476,12 @@ public class JdbcOperationsSessionRepository
private class ExpiringSessionMapper implements RowMapper<ExpiringSession> {
public ExpiringSession mapRow(ResultSet rs, int rowNum) throws SQLException {
return (ExpiringSession) JdbcOperationsSessionRepository.this.conversionService.convert(
JdbcOperationsSessionRepository.this.lobHandler.getBlobAsBytes(rs, "SESSION_BYTES"), TypeDescriptor.valueOf(byte[].class), TypeDescriptor.valueOf(ExpiringSession.class));
return (ExpiringSession) JdbcOperationsSessionRepository.this.conversionService
.convert(
JdbcOperationsSessionRepository.this.lobHandler
.getBlobAsBytes(rs, "SESSION_BYTES"),
TypeDescriptor.valueOf(byte[].class),
TypeDescriptor.valueOf(ExpiringSession.class));
}
}

View File

@@ -28,8 +28,8 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
/**
* Add this annotation to an {@code @Configuration} class to expose the
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and
* backed by a relational database. In order to leverage the annotation, a single
* SessionRepositoryFilter as a bean named "springSessionRepositoryFilter" and backed by a
* relational database. In order to leverage the annotation, a single
* {@link javax.sql.DataSource} must be provided. For example:
*
* <pre class="code">
@@ -59,8 +59,8 @@ import org.springframework.session.config.annotation.web.http.EnableSpringHttpSe
* @since 1.2.0
* @see EnableSpringHttpSession
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(JdbcHttpSessionConfiguration.class)
@Configuration

View File

@@ -49,7 +49,8 @@ import org.springframework.util.StringUtils;
*/
@Configuration
@EnableScheduling
public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements ImportAware {
private String tableName = "";
@@ -71,19 +72,21 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
@Bean
public JdbcOperationsSessionRepository sessionRepository(
@Qualifier("springSessionJdbcOperations") JdbcOperations jdbcOperations) {
JdbcOperationsSessionRepository sessionRepository =
new JdbcOperationsSessionRepository(jdbcOperations);
JdbcOperationsSessionRepository sessionRepository = new JdbcOperationsSessionRepository(
jdbcOperations);
String tableName = getTableName();
if (StringUtils.hasText(tableName)) {
sessionRepository.setTableName(tableName);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
sessionRepository
.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (this.lobHandler != null) {
sessionRepository.setLobHandler(this.lobHandler);
}
if (this.springSessionConversionService != null) {
sessionRepository.setConversionService(this.springSessionConversionService);
} else if(conversionService != null) {
}
else if (this.conversionService != null) {
sessionRepository.setConversionService(this.conversionService);
}
return sessionRepository;
@@ -109,10 +112,12 @@ public class JdbcHttpSessionConfiguration extends SpringHttpSessionConfiguration
}
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableAttrMap = importMetadata.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
Map<String, Object> enableAttrMap = importMetadata
.getAnnotationAttributes(EnableJdbcHttpSession.class.getName());
AnnotationAttributes enableAttrs = AnnotationAttributes.fromMap(enableAttrMap);
this.tableName = enableAttrs.getString("tableName");
this.maxInactiveIntervalInSeconds = enableAttrs.getNumber("maxInactiveIntervalInSeconds");
this.maxInactiveIntervalInSeconds = enableAttrs
.getNumber("maxInactiveIntervalInSeconds");
}
}

View File

@@ -38,32 +38,31 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
import org.springframework.web.filter.DelegatingFilterProxy;
/**
* Registers the {@link DelegatingFilterProxy} to use the
* springSessionRepositoryFilter before any other registered {@link Filter}. When
* used with {@link #AbstractHttpSessionApplicationInitializer(Class...)}, it
* will also register a {@link ContextLoaderListener}. When used with
* {@link #AbstractHttpSessionApplicationInitializer()}, this class is typically
* used in addition to a subclass of {@link AbstractContextLoaderInitializer}.
* Registers the {@link DelegatingFilterProxy} to use the springSessionRepositoryFilter
* before any other registered {@link Filter}. When used with
* {@link #AbstractHttpSessionApplicationInitializer(Class...)}, it will also register a
* {@link ContextLoaderListener}. When used with
* {@link #AbstractHttpSessionApplicationInitializer()}, this class is typically used in
* addition to a subclass of {@link AbstractContextLoaderInitializer}.
*
* <p>
* By default the {@link DelegatingFilterProxy} is registered with support for
* asynchronous requests, but can be enabled by overriding {@link #isAsyncSessionSupported()} and
* {@link #getSessionDispatcherTypes()}.
* asynchronous requests, but can be enabled by overriding
* {@link #isAsyncSessionSupported()} and {@link #getSessionDispatcherTypes()}.
* </p>
*
* <p>
* Additional configuration before and after the springSecurityFilterChain can
* be added by overriding
* {@link #afterSessionRepositoryFilter(ServletContext)}.
* Additional configuration before and after the springSecurityFilterChain can be added by
* overriding {@link #afterSessionRepositoryFilter(ServletContext)}.
* </p>
*
*
* <h2>Caveats</h2>
* <p>
* Subclasses of {@code AbstractDispatcherServletInitializer} will register their
* filters before any other {@link Filter}. This means that you will typically
* want to ensure subclasses of {@code AbstractDispatcherServletInitializer} are invoked
* first. This can be done by ensuring the {@link Order} or {@link Ordered} of
* Subclasses of {@code AbstractDispatcherServletInitializer} will register their filters
* before any other {@link Filter}. This means that you will typically want to ensure
* subclasses of {@code AbstractDispatcherServletInitializer} are invoked first. This can
* be done by ensuring the {@link Order} or {@link Ordered} of
* {@code AbstractDispatcherServletInitializer} are sooner than subclasses of
* {@code AbstractSecurityWebApplicationInitializer}.
* </p>
@@ -72,7 +71,8 @@ import org.springframework.web.filter.DelegatingFilterProxy;
*
*/
@Order(100)
public abstract class AbstractHttpSessionApplicationInitializer implements WebApplicationInitializer {
public abstract class AbstractHttpSessionApplicationInitializer
implements WebApplicationInitializer {
private static final String SERVLET_CONTEXT_PREFIX = "org.springframework.web.servlet.FrameworkServlet.CONTEXT.";
@@ -83,11 +83,10 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
private final Class<?>[] configurationClasses;
/**
* Creates a new instance that assumes the Spring Session configuration is
* loaded by some other means than this class. For example, a user might
* create a {@link ContextLoaderListener} using a subclass of
* Creates a new instance that assumes the Spring Session configuration is loaded by
* some other means than this class. For example, a user might create a
* {@link ContextLoaderListener} using a subclass of
* {@link AbstractContextLoaderInitializer}.
*
* @see ContextLoaderListener
@@ -97,17 +96,18 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
}
/**
* Creates a new instance that will instantiate the
* {@link ContextLoaderListener} with the specified classes.
* Creates a new instance that will instantiate the {@link ContextLoaderListener} with
* the specified classes.
*
* @param configurationClasses {@code @Configuration} classes that will be used to configure the context
* @param configurationClasses {@code @Configuration} classes that will be used to
* configure the context
*/
protected AbstractHttpSessionApplicationInitializer(Class<?>... configurationClasses) {
protected AbstractHttpSessionApplicationInitializer(
Class<?>... configurationClasses) {
this.configurationClasses = configurationClasses;
}
public void onStartup(ServletContext servletContext)
throws ServletException {
public void onStartup(ServletContext servletContext) throws ServletException {
beforeSessionRepositoryFilter(servletContext);
if (this.configurationClasses != null) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
@@ -124,7 +124,8 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
*/
private void insertSessionRepositoryFilter(ServletContext servletContext) {
String filterName = DEFAULT_FILTER_NAME;
DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(filterName);
DelegatingFilterProxy springSessionRepositoryFilter = new DelegatingFilterProxy(
filterName);
String contextAttribute = getWebApplicationContextAttribute();
if (contextAttribute != null) {
springSessionRepositoryFilter.setContextAttribute(contextAttribute);
@@ -133,28 +134,24 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
}
/**
* Inserts the provided {@link Filter}s before existing {@link Filter}s
* using default generated names, {@link #getSessionDispatcherTypes()}, and
* Inserts the provided {@link Filter}s before existing {@link Filter}s using default
* generated names, {@link #getSessionDispatcherTypes()}, and
* {@link #isAsyncSessionSupported()}.
*
* @param servletContext
* the {@link ServletContext} to use
* @param filters
* the {@link Filter}s to register
* @param servletContext the {@link ServletContext} to use
* @param filters the {@link Filter}s to register
*/
protected final void insertFilters(ServletContext servletContext, Filter... filters) {
registerFilters(servletContext, true, filters);
}
/**
* Inserts the provided {@link Filter}s after existing {@link Filter}s
* using default generated names, {@link #getSessionDispatcherTypes()}, and
* Inserts the provided {@link Filter}s after existing {@link Filter}s using default
* generated names, {@link #getSessionDispatcherTypes()}, and
* {@link #isAsyncSessionSupported()}.
*
* @param servletContext
* the {@link ServletContext} to use
* @param filters
* the {@link Filter}s to register
* @param servletContext the {@link ServletContext} to use
* @param filters the {@link Filter}s to register
*/
protected final void appendFilters(ServletContext servletContext, Filter... filters) {
registerFilters(servletContext, false, filters);
@@ -162,24 +159,23 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
/**
* Registers the provided {@link Filter}s using default generated names,
* {@link #getSessionDispatcherTypes()}, and
* {@link #isAsyncSessionSupported()}.
* {@link #getSessionDispatcherTypes()}, and {@link #isAsyncSessionSupported()}.
*
* @param servletContext
* the {@link ServletContext} to use
* @param insertBeforeOtherFilters
* if true, will insert the provided {@link Filter}s before other
* {@link Filter}s. Otherwise, will insert the {@link Filter}s
* after other {@link Filter}s.
* @param filters
* the {@link Filter}s to register
* @param servletContext the {@link ServletContext} to use
* @param insertBeforeOtherFilters if true, will insert the provided {@link Filter}s
* before other {@link Filter}s. Otherwise, will insert the {@link Filter}s after
* other {@link Filter}s.
* @param filters the {@link Filter}s to register
*/
private void registerFilters(ServletContext servletContext, boolean insertBeforeOtherFilters, Filter... filters) {
private void registerFilters(ServletContext servletContext,
boolean insertBeforeOtherFilters, Filter... filters) {
Assert.notEmpty(filters, "filters cannot be null or empty");
for (Filter filter : filters) {
if (filter == null) {
throw new IllegalArgumentException("filters cannot contain null values. Got " + Arrays.asList(filters));
throw new IllegalArgumentException(
"filters cannot contain null values. Got "
+ Arrays.asList(filters));
}
String filterName = Conventions.getVariableName(filter);
registerFilter(servletContext, insertBeforeOtherFilters, filterName, filter);
@@ -187,37 +183,43 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
}
/**
* Registers the provided filter using the {@link #isAsyncSessionSupported()} and {@link #getSessionDispatcherTypes()}.
* Registers the provided filter using the {@link #isAsyncSessionSupported()} and
* {@link #getSessionDispatcherTypes()}.
*
* @param servletContext the servlet context
* @param insertBeforeOtherFilters should this Filter be inserted before or after other {@link Filter}
* @param insertBeforeOtherFilters should this Filter be inserted before or after
* other {@link Filter}
* @param filterName the filter name
* @param filter the filter
*/
private void registerFilter(ServletContext servletContext, boolean insertBeforeOtherFilters, String filterName, Filter filter) {
private void registerFilter(ServletContext servletContext,
boolean insertBeforeOtherFilters, String filterName, Filter filter) {
Dynamic registration = servletContext.addFilter(filterName, filter);
if (registration == null) {
throw new IllegalStateException("Duplicate Filter registration for '" + filterName + "'. Check to ensure the Filter is only configured once.");
throw new IllegalStateException(
"Duplicate Filter registration for '" + filterName
+ "'. Check to ensure the Filter is only configured once.");
}
registration.setAsyncSupported(isAsyncSessionSupported());
EnumSet<DispatcherType> dispatcherTypes = getSessionDispatcherTypes();
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters, "/*");
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
"/*");
}
/**
* Returns the {@link DelegatingFilterProxy#getContextAttribute()} or null
* if the parent {@link ApplicationContext} should be used. The default
* behavior is to use the parent {@link ApplicationContext}.
* Returns the {@link DelegatingFilterProxy#getContextAttribute()} or null if the
* parent {@link ApplicationContext} should be used. The default behavior is to use
* the parent {@link ApplicationContext}.
*
* <p>
* If {@link #getDispatcherWebApplicationContextSuffix()} is non-null the
* {@link WebApplicationContext} for the Dispatcher will be used. This means
* the child {@link ApplicationContext} is used to look up the
* springSessionRepositoryFilter bean.
* {@link WebApplicationContext} for the Dispatcher will be used. This means the child
* {@link ApplicationContext} is used to look up the springSessionRepositoryFilter
* bean.
* </p>
*
* @return the {@link DelegatingFilterProxy#getContextAttribute()} or null
* if the parent {@link ApplicationContext} should be used
* @return the {@link DelegatingFilterProxy#getContextAttribute()} or null if the
* parent {@link ApplicationContext} should be used
*/
private String getWebApplicationContextAttribute() {
String dispatcherServletName = getDispatcherWebApplicationContextSuffix();
@@ -227,23 +229,21 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
return SERVLET_CONTEXT_PREFIX + dispatcherServletName;
}
/**
* Return the {@code <servlet-name>} to use the DispatcherServlet's
* {@link WebApplicationContext} to find the {@link DelegatingFilterProxy}
* or null to use the parent {@link ApplicationContext}.
* {@link WebApplicationContext} to find the {@link DelegatingFilterProxy} or null to
* use the parent {@link ApplicationContext}.
*
* <p>
* For example, if you are using AbstractDispatcherServletInitializer or
* AbstractAnnotationConfigDispatcherServletInitializer and using the
* provided Servlet name, you can return "dispatcher" from this method to
* use the DispatcherServlet's {@link WebApplicationContext}.
* AbstractAnnotationConfigDispatcherServletInitializer and using the provided Servlet
* name, you can return "dispatcher" from this method to use the DispatcherServlet's
* {@link WebApplicationContext}.
* </p>
*
* @return the {@code <servlet-name>} of the DispatcherServlet to use its
* {@link WebApplicationContext} or null (default) to use the parent
* {@link ApplicationContext}.
* {@link WebApplicationContext} or null (default) to use the parent
* {@link ApplicationContext}.
*/
protected String getDispatcherWebApplicationContextSuffix() {
return null;
@@ -270,15 +270,15 @@ public abstract class AbstractHttpSessionApplicationInitializer implements WebAp
* @return the {@link DispatcherType} for the filter
*/
protected EnumSet<DispatcherType> getSessionDispatcherTypes() {
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC);
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR,
DispatcherType.ASYNC);
}
/**
* Determine if the springSessionRepositoryFilter should be marked as supporting
* asynch. Default is true.
*
* @return true if springSessionRepositoryFilter should be marked as supporting
* asynch
* @return true if springSessionRepositoryFilter should be marked as supporting asynch
*/
protected boolean isAsyncSessionSupported() {
return true;

View File

@@ -37,23 +37,21 @@ import org.springframework.util.Assert;
/**
* A {@link HttpSessionStrategy} that uses a cookie to obtain the session from.
* Specifically, this implementation will allow specifying a cookie name using
* {@link CookieHttpSessionStrategy#setCookieName(String)}. The default is
* "SESSION".
* {@link CookieHttpSessionStrategy#setCookieName(String)}. The default is "SESSION".
*
* When a session is created, the HTTP response will have a cookie with the
* specified cookie name and the value of the session id. The cookie will be
* marked as a session cookie, use the context path for the path of the cookie,
* marked as HTTPOnly, and if
* {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the
* cookie will be marked as secure. For example:
* When a session is created, the HTTP response will have a cookie with the specified
* cookie name and the value of the session id. The cookie will be marked as a session
* cookie, use the context path for the path of the cookie, marked as HTTPOnly, and if
* {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the cookie will
* be marked as secure. For example:
*
* <pre>
* HTTP/1.1 200 OK
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Path=/context-root; Secure; HttpOnly
* </pre>
*
* The client should now include the session in each request by specifying the
* same cookie in their request. For example:
* The client should now include the session in each request by specifying the same cookie
* in their request. For example:
*
* <pre>
* GET /messages/ HTTP/1.1
@@ -61,8 +59,8 @@ import org.springframework.util.Assert;
* Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
* </pre>
*
* When the session is invalidated, the server will send an HTTP response that
* expires the cookie. For example:
* When the session is invalidated, the server will send an HTTP response that expires the
* cookie. For example:
*
* <pre>
* HTTP/1.1 200 OK
@@ -72,10 +70,9 @@ import org.springframework.util.Assert;
* <h2>Supporting Multiple Simultaneous Sessions</h2>
*
* <p>
* By default multiple sessions are also supported. Once a session is
* established with the browser, another session can be initiated by specifying
* a unique value for the {@link #setSessionAliasParamName(String)}. For
* example, a request to:
* By default multiple sessions are also supported. Once a session is established with the
* browser, another session can be initiated by specifying a unique value for the
* {@link #setSessionAliasParamName(String)}. For example, a request to:
* </p>
*
* <pre>
@@ -92,70 +89,58 @@ import org.springframework.util.Assert;
* </pre>
*
* <p>
* To use the original session a request without the HTTP parameter u can be
* made. To use the new session, a request with the HTTP parameter
* _s=1416195761178 can be used. By default URLs will be rewritten to include the
* currently selected session.
* To use the original session a request without the HTTP parameter u can be made. To use
* the new session, a request with the HTTP parameter _s=1416195761178 can be used. By
* default URLs will be rewritten to include the currently selected session.
* </p>
*
* <h2>Selecting Sessions</h2>
*
* <p>
* Sessions can be managed by using the HttpSessionManager and
* SessionRepository. If you are not using Spring in the rest of your
* application you can obtain a reference from the HttpServletRequest
* attributes. An example is provided below:
* Sessions can be managed by using the HttpSessionManager and SessionRepository. If you
* are not using Spring in the rest of your application you can obtain a reference from
* the HttpServletRequest attributes. An example is provided below:
* </p>
*
* {@code
* <code>
* HttpSessionManager sessionManager =
* (HttpSessionManager) req.getAttribute(HttpSessionManager.class.getName());
* SessionRepository<Session> repo =
* (SessionRepository<Session>) req.getAttribute(SessionRepository.class.getName());
* SessionRepository&lt;Session&gt; repo =
* (SessionRepository&lt;Session&gt;) req.getAttribute(SessionRepository.class.getName());
*
* String currentSessionAlias = sessionManager.getCurrentSessionAlias(req);
* Map<String, String> sessionIds = sessionManager.getSessionIds(req);
* Map&lt;String, String&gt; sessionIds = sessionManager.getSessionIds(req);
* String newSessionAlias = String.valueOf(System.currentTimeMillis());
*
* String contextPath = req.getContextPath();
* List<Account> accounts = new ArrayList<>();
* Account currentAccount = null;
* for(Map.Entry<String, String> entry : sessionIds.entrySet()) {
* String alias = entry.getKey();
* String sessionId = entry.getValue();
* List&lt;Account&gt; accounts = new ArrayList&lt;&gt;();
* Account currentAccount = null; for(Map.Entry&lt;String, String&gt; entry :
* sessionIds.entrySet()) { String alias = entry.getKey(); String sessionId =
* entry.getValue();
* </code>
*
* Session session = repo.getSession(sessionId);
* if(session == null) {
* continue;
* }
* Session session = repo.getSession(sessionId); if(session == null) { continue; }
*
* String username = session.getAttribute("username");
* if(username == null) {
* newSessionAlias = alias;
* continue;
* }
* String username = session.getAttribute("username"); if(username == null) {
* newSessionAlias = alias; continue; }
*
* String logoutUrl = sessionManager.encodeURL("./logout", alias);
* String switchAccountUrl = sessionManager.encodeURL("./", alias);
* Account account = new Account(username, logoutUrl, switchAccountUrl);
* if(currentSessionAlias.equals(alias)) {
* currentAccount = account;
* } else {
* accounts.add(account);
* }
* }
* String logoutUrl = sessionManager.encodeURL("./logout", alias); String switchAccountUrl
* = sessionManager.encodeURL("./", alias); Account account = new Account(username,
* logoutUrl, switchAccountUrl); if(currentSessionAlias.equals(alias)) { currentAccount =
* account; } else { accounts.add(account); } }
*
* req.setAttribute("currentAccount", currentAccount);
* req.setAttribute("addAccountUrl", sessionManager.encodeURL(contextPath, newSessionAlias));
* req.setAttribute("accounts", accounts);
* }
* req.setAttribute("currentAccount", currentAccount); req.setAttribute("addAccountUrl",
* sessionManager.encodeURL(contextPath, newSessionAlias)); req.setAttribute("accounts",
* accounts); }
*
*
* @author Rob Winch
* @since 1.0
*/
public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy, HttpSessionManager {
private static final String SESSION_IDS_WRITTEN_ATTR = CookieHttpSessionStrategy.class.getName().concat(".SESSIONS_WRITTEN_ATTR");
public final class CookieHttpSessionStrategy
implements MultiHttpSessionStrategy, HttpSessionManager {
private static final String SESSION_IDS_WRITTEN_ATTR = CookieHttpSessionStrategy.class
.getName().concat(".SESSIONS_WRITTEN_ATTR");
static final String DEFAULT_ALIAS = "0";
@@ -211,7 +196,8 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
}
}
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
public void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response) {
Set<String> sessionIdsWritten = getSessionIdsWritten(request);
if (sessionIdsWritten.contains(session.getId())) {
return;
@@ -223,12 +209,14 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
sessionIds.put(sessionAlias, session.getId());
String cookieValue = createSessionCookieValue(sessionIds);
this.cookieSerializer.writeCookieValue(new CookieValue(request, response, cookieValue));
this.cookieSerializer
.writeCookieValue(new CookieValue(request, response, cookieValue));
}
@SuppressWarnings("unchecked")
private Set<String> getSessionIdsWritten(HttpServletRequest request) {
Set<String> sessionsWritten = (Set<String>) request.getAttribute(SESSION_IDS_WRITTEN_ATTR);
Set<String> sessionsWritten = (Set<String>) request
.getAttribute(SESSION_IDS_WRITTEN_ATTR);
if (sessionsWritten == null) {
sessionsWritten = new HashSet<String>();
request.setAttribute(SESSION_IDS_WRITTEN_ATTR, sessionsWritten);
@@ -258,24 +246,23 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
return buffer.toString();
}
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
public void onInvalidateSession(HttpServletRequest request,
HttpServletResponse response) {
Map<String, String> sessionIds = getSessionIds(request);
String requestedAlias = getCurrentSessionAlias(request);
sessionIds.remove(requestedAlias);
String cookieValue = createSessionCookieValue(sessionIds);
this.cookieSerializer.writeCookieValue(new CookieValue(request, response, cookieValue));
this.cookieSerializer
.writeCookieValue(new CookieValue(request, response, cookieValue));
}
/**
* Sets the name of the HTTP parameter that is used to specify the session
* alias. If the value is null, then only a single session is supported per
* browser.
* Sets the name of the HTTP parameter that is used to specify the session alias. If
* the value is null, then only a single session is supported per browser.
*
* @param sessionAliasParamName
* the name of the HTTP parameter used to specify the session
* alias. If null, then ony a single session is supported per
* browser.
* @param sessionAliasParamName the name of the HTTP parameter used to specify the
* session alias. If null, then ony a single session is supported per browser.
*/
public void setSessionAliasParamName(String sessionAliasParamName) {
this.sessionParam = sessionAliasParamName;
@@ -305,7 +292,8 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
public Map<String, String> getSessionIds(HttpServletRequest request) {
List<String> cookieValues = this.cookieSerializer.readCookieValues(request);
String sessionCookieValue = cookieValues.isEmpty() ? "" : cookieValues.iterator().next();
String sessionCookieValue = cookieValues.isEmpty() ? ""
: cookieValues.iterator().next();
Map<String, String> result = new LinkedHashMap<String, String>();
StringTokenizer tokens = new StringTokenizer(sessionCookieValue, " ");
if (tokens.countTokens() == 1) {
@@ -323,12 +311,14 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
return result;
}
public HttpServletRequest wrapRequest(HttpServletRequest request, HttpServletResponse response) {
public HttpServletRequest wrapRequest(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute(HttpSessionManager.class.getName(), this);
return request;
}
public HttpServletResponse wrapResponse(HttpServletRequest request, HttpServletResponse response) {
public HttpServletResponse wrapResponse(HttpServletRequest request,
HttpServletResponse response) {
return new MultiSessionHttpServletResponse(response, request);
}
@@ -337,12 +327,14 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
int queryStart = url.indexOf("?");
boolean isDefaultAlias = DEFAULT_ALIAS.equals(encodedSessionAlias);
if (queryStart < 0) {
return isDefaultAlias ? url : url + "?" + this.sessionParam + "=" + encodedSessionAlias;
return isDefaultAlias ? url
: url + "?" + this.sessionParam + "=" + encodedSessionAlias;
}
String path = url.substring(0, queryStart);
String query = url.substring(queryStart + 1, url.length());
String replacement = isDefaultAlias ? "" : "$1" + encodedSessionAlias;
query = query.replaceFirst("((^|&)" + this.sessionParam + "=)([^&]+)?", replacement);
query = query.replaceFirst("((^|&)" + this.sessionParam + "=)([^&]+)?",
replacement);
if (!isDefaultAlias && url.endsWith(query)) {
// no existing alias
if (!(query.endsWith("&") || query.length() == 0)) {
@@ -369,7 +361,8 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
class MultiSessionHttpServletResponse extends HttpServletResponseWrapper {
private final HttpServletRequest request;
MultiSessionHttpServletResponse(HttpServletResponse response, HttpServletRequest request) {
MultiSessionHttpServletResponse(HttpServletResponse response,
HttpServletRequest request) {
super(response);
this.request = request;
}
@@ -377,7 +370,8 @@ public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy
@Override
public String encodeRedirectURL(String url) {
url = super.encodeRedirectURL(url);
return CookieHttpSessionStrategy.this.encodeURL(url, getCurrentSessionAlias(this.request));
return CookieHttpSessionStrategy.this.encodeURL(url,
getCurrentSessionAlias(this.request));
}
@Override

View File

@@ -23,8 +23,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Strategy for reading and writing a cookie value to the
* {@link HttpServletResponse}.
* Strategy for reading and writing a cookie value to the {@link HttpServletResponse}.
*
* @author Rob Winch
* @since 1.1
@@ -32,25 +31,21 @@ import javax.servlet.http.HttpServletResponse;
public interface CookieSerializer {
/**
* Writes a given {@link CookieValue} to the provided
* {@link HttpServletResponse}.
* Writes a given {@link CookieValue} to the provided {@link HttpServletResponse}.
*
* @param cookieValue
* the {@link CookieValue} to write to
* {@link CookieValue#getResponse()}. Cannot be null.
* @param cookieValue the {@link CookieValue} to write to
* {@link CookieValue#getResponse()}. Cannot be null.
*/
void writeCookieValue(CookieValue cookieValue);
/**
* Reads all the matching cookies from the {@link HttpServletRequest}. The
* result is a List since there can be multiple {@link Cookie} in a single
* request with a matching name. For example, one Cookie may have a path of
* / and another of /context, but the path is not transmitted in the
* request.
* Reads all the matching cookies from the {@link HttpServletRequest}. The result is a
* List since there can be multiple {@link Cookie} in a single request with a matching
* name. For example, one Cookie may have a path of / and another of /context, but the
* path is not transmitted in the request.
*
* @param request
* the {@link HttpServletRequest} to read the cookie from. Cannot
* be null.
* @param request the {@link HttpServletRequest} to read the cookie from. Cannot be
* null.
* @return the values of all the matching cookies
*/
List<String> readCookieValues(HttpServletRequest request);
@@ -70,19 +65,15 @@ public interface CookieSerializer {
/**
* Creates a new instance.
*
* @param request
* the {@link HttpServletRequest} to use. Useful for
* determining the context in which the cookie is set. Cannot
* be null.
* @param response
* the {@link HttpServletResponse} to use.
* @param cookieValue
* the value of the cookie to be written. This value may be
* modified by the {@link CookieSerializer} when writing to
* the actual cookie so long as the original value is
* returned when the cookie is read.
* @param request the {@link HttpServletRequest} to use. Useful for determining
* the context in which the cookie is set. Cannot be null.
* @param response the {@link HttpServletResponse} to use.
* @param cookieValue the value of the cookie to be written. This value may be
* modified by the {@link CookieSerializer} when writing to the actual cookie so
* long as the original value is returned when the cookie is read.
*/
public CookieValue(HttpServletRequest request, HttpServletResponse response, String cookieValue) {
public CookieValue(HttpServletRequest request, HttpServletResponse response,
String cookieValue) {
this.request = request;
this.response = response;
this.cookieValue = cookieValue;
@@ -105,7 +96,9 @@ public interface CookieSerializer {
}
/**
* The value to be written. This value may be modified by the {@link CookieSerializer} before written to the cookie. However, the value must be the same as the original when it is read back in.
* The value to be written. This value may be modified by the
* {@link CookieSerializer} before written to the cookie. However, the value must
* be the same as the original when it is read back in.
*
* @return the value to be written
*/

View File

@@ -51,7 +51,9 @@ public class DefaultCookieSerializer implements CookieSerializer {
/*
* (non-Javadoc)
* @see org.springframework.session.web.http.CookieSerializer#readCookieValues(javax.servlet.http.HttpServletRequest)
*
* @see org.springframework.session.web.http.CookieSerializer#readCookieValues(javax.
* servlet.http.HttpServletRequest)
*/
public List<String> readCookieValues(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
@@ -64,7 +66,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
continue;
}
if (this.jvmRoute != null && sessionId.endsWith(this.jvmRoute)) {
sessionId = sessionId.substring(0, sessionId.length() - this.jvmRoute.length());
sessionId = sessionId.substring(0,
sessionId.length() - this.jvmRoute.length());
}
matchingCookieValues.add(sessionId);
}
@@ -75,8 +78,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
/*
* (non-Javadoc)
* @see
* org.springframework.session.web.http.CookieWriter#writeCookieValue(org.
*
* @see org.springframework.session.web.http.CookieWriter#writeCookieValue(org.
* springframework.session.web.http.CookieWriter.CookieValue)
*/
public void writeCookieValue(CookieValue cookieValue) {
@@ -84,7 +87,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
HttpServletResponse response = cookieValue.getResponse();
String requestedCookieValue = cookieValue.getCookieValue();
String actualCookieValue = this.jvmRoute == null ? requestedCookieValue : requestedCookieValue + this.jvmRoute;
String actualCookieValue = this.jvmRoute == null ? requestedCookieValue
: requestedCookieValue + this.jvmRoute;
Cookie sessionCookie = new Cookie(this.cookieName, actualCookieValue);
sessionCookie.setSecure(isSecureCookie(request));
@@ -109,26 +113,25 @@ public class DefaultCookieSerializer implements CookieSerializer {
}
/**
* Sets if a Cookie marked as secure should be used. The default is to use
* the value of {@link HttpServletRequest#isSecure()}.
* Sets if a Cookie marked as secure should be used. The default is to use the value
* of {@link HttpServletRequest#isSecure()}.
*
* @param useSecureCookie
* determines if the cookie should be marked as secure.
* @param useSecureCookie determines if the cookie should be marked as secure.
*/
public void setUseSecureCookie(boolean useSecureCookie) {
this.useSecureCookie = useSecureCookie;
}
/**
* Sets if a Cookie marked as HTTP Only should be used. The default is true
* in Servlet 3+ environments, else false.
* Sets if a Cookie marked as HTTP Only should be used. The default is true in Servlet
* 3+ environments, else false.
*
* @param useHttpOnlyCookie
* determines if the cookie should be marked as HTTP Only.
* @param useHttpOnlyCookie determines if the cookie should be marked as HTTP Only.
*/
public void setUseHttpOnlyCookie(boolean useHttpOnlyCookie) {
if (useHttpOnlyCookie && !isServlet3()) {
throw new IllegalArgumentException("You cannot set useHttpOnlyCookie to true in pre Servlet 3 environment");
throw new IllegalArgumentException(
"You cannot set useHttpOnlyCookie to true in pre Servlet 3 environment");
}
this.useHttpOnlyCookie = useHttpOnlyCookie;
}
@@ -141,12 +144,11 @@ public class DefaultCookieSerializer implements CookieSerializer {
}
/**
* Sets the path of the Cookie. The default is to use the context path from
* the {@link HttpServletRequest}.
* Sets the path of the Cookie. The default is to use the context path from the
* {@link HttpServletRequest}.
*
* @param cookiePath
* the path of the Cookie. If null, the default of the context
* path will be used.
* @param cookiePath the path of the Cookie. If null, the default of the context path
* will be used.
*/
public void setCookiePath(String cookiePath) {
this.cookiePath = cookiePath;
@@ -160,29 +162,27 @@ public class DefaultCookieSerializer implements CookieSerializer {
}
/**
* Sets the maxAge property of the Cookie. The default is -1 which signals
* to delete the cookie when the browser is closed.
* Sets the maxAge property of the Cookie. The default is -1 which signals to delete
* the cookie when the browser is closed.
*
* @param cookieMaxAge
* the maxAge property of the Cookie
* @param cookieMaxAge the maxAge property of the Cookie
*/
public void setCookieMaxAge(int cookieMaxAge) {
this.cookieMaxAge = cookieMaxAge;
}
/**
* Sets an explicit Domain Name. This allow the domain of "example.com" to
* be used when the request comes from www.example.com. This allows for
* sharing the cookie across subdomains. The default is to use the current
* domain.
* Sets an explicit Domain Name. This allow the domain of "example.com" to be used
* when the request comes from www.example.com. This allows for sharing the cookie
* across subdomains. The default is to use the current domain.
*
* @param domainName
* the name of the domain to use. (i.e. "example.com")
* @param domainName the name of the domain to use. (i.e. "example.com")
* @throws IllegalStateException if the domainNamePattern is also set
*/
public void setDomainName(String domainName) {
if (this.domainNamePattern != null) {
throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
throw new IllegalStateException(
"Cannot set both domainName and domainNamePattern");
}
this.domainName = domainName;
}
@@ -190,23 +190,20 @@ public class DefaultCookieSerializer implements CookieSerializer {
/**
* <p>
* Sets a case insensitive pattern used to extract the domain name from the
* {@link HttpServletRequest#getServerName()}. The pattern should provide a
* single grouping that defines what the value is that should be matched.
* User's should be careful not to output malicious characters like new
* lines to prevent from things like
* <a href= "https://www.owasp.org/index.php/HTTP_Response_Splitting">HTTP
* {@link HttpServletRequest#getServerName()}. The pattern should provide a single
* grouping that defines what the value is that should be matched. User's should be
* careful not to output malicious characters like new lines to prevent from things
* like <a href= "https://www.owasp.org/index.php/HTTP_Response_Splitting">HTTP
* Response Splitting</a>.
* </p>
*
* <p>
* If the pattern does not match, then no domain will be set. This is useful
* to ensure the domain is not set during development when localhost might
* be used.
* If the pattern does not match, then no domain will be set. This is useful to ensure
* the domain is not set during development when localhost might be used.
* </p>
* <p>
* An example value might be "^.+?\\.(\\w+\\.[a-z]+)$". For the given input,
* it would provide the following explicit domain (null means no domain name
* is set):
* An example value might be "^.+?\\.(\\w+\\.[a-z]+)$". For the given input, it would
* provide the following explicit domain (null means no domain name is set):
* </p>
*
* <ul>
@@ -216,33 +213,35 @@ public class DefaultCookieSerializer implements CookieSerializer {
* <li>127.0.1.1 - null</li>
* </ul>
*
* @param domainNamePattern
* the case insensitive pattern to extract the domain name with
* @param domainNamePattern the case insensitive pattern to extract the domain name
* with
* @throws IllegalStateException if the domainName is also set
*/
public void setDomainNamePattern(String domainNamePattern) {
if (this.domainName != null) {
throw new IllegalStateException("Cannot set both domainName and domainNamePattern");
throw new IllegalStateException(
"Cannot set both domainName and domainNamePattern");
}
this.domainNamePattern = Pattern.compile(domainNamePattern, Pattern.CASE_INSENSITIVE);
this.domainNamePattern = Pattern.compile(domainNamePattern,
Pattern.CASE_INSENSITIVE);
}
/**
* <p>
* Used to identify which JVM to route to for session affinity. With some
* implementations (i.e. Redis) this provides no performance benefit.
* However, this can help with tracing logs of a particular user. This will ensure that the value of the cookie is formatted as
* implementations (i.e. Redis) this provides no performance benefit. However, this
* can help with tracing logs of a particular user. This will ensure that the value of
* the cookie is formatted as
* </p>
* <code>
* sessionId + "." jvmRoute
* </code>
* <p>
* To use set a custom route on each JVM instance and setup a frontend proxy
* to forward all requests to the JVM based on the route.
* To use set a custom route on each JVM instance and setup a frontend proxy to
* forward all requests to the JVM based on the route.
* </p>
*
* @param jvmRoute
* the JVM Route to use (i.e. "node01jvmA", "n01ja", etc)
* @param jvmRoute the JVM Route to use (i.e. "node01jvmA", "n01ja", etc)
*/
public void setJvmRoute(String jvmRoute) {
this.jvmRoute = "." + jvmRoute;

View File

@@ -138,7 +138,8 @@ class ExpiringSessionHttpSession<S extends ExpiringSession> implements HttpSessi
private void checkState() {
if (this.invalidated) {
throw new IllegalStateException("The HttpSession has already be invalidated.");
throw new IllegalStateException(
"The HttpSession has already be invalidated.");
}
}

View File

@@ -23,17 +23,20 @@ import org.springframework.session.Session;
import org.springframework.util.Assert;
/**
* A {@link HttpSessionStrategy} that uses a header to obtain the session from. Specifically, this implementation will
* allow specifying a header name using {@link HeaderHttpSessionStrategy#setHeaderName(String)}. The default is "x-auth-token".
* A {@link HttpSessionStrategy} that uses a header to obtain the session from.
* Specifically, this implementation will allow specifying a header name using
* {@link HeaderHttpSessionStrategy#setHeaderName(String)}. The default is "x-auth-token".
*
* When a session is created, the HTTP response will have a response header of the specified name and the value of the session id. For example:
* When a session is created, the HTTP response will have a response header of the
* specified name and the value of the session id. For example:
*
* <pre>
* HTTP/1.1 200 OK
* x-auth-token: f81d4fae-7dec-11d0-a765-00a0c91e6bf6
* </pre>
*
* The client should now include the session in each request by specifying the same header in their request. For example:
* The client should now include the session in each request by specifying the same header
* in their request. For example:
*
* <pre>
* GET /messages/ HTTP/1.1
@@ -41,7 +44,8 @@ import org.springframework.util.Assert;
* x-auth-token: f81d4fae-7dec-11d0-a765-00a0c91e6bf6
* </pre>
*
* When the session is invalidated, the server will send an HTTP response that has the header name and a blank value. For example:
* When the session is invalidated, the server will send an HTTP response that has the
* header name and a blank value. For example:
*
* <pre>
* HTTP/1.1 200 OK
@@ -58,11 +62,13 @@ public class HeaderHttpSessionStrategy implements HttpSessionStrategy {
return request.getHeader(this.headerName);
}
public void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response) {
public void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response) {
response.setHeader(this.headerName, session.getId());
}
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
public void onInvalidateSession(HttpServletRequest request,
HttpServletResponse response) {
response.setHeader(this.headerName, "");
}

View File

@@ -21,8 +21,8 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
/**
* Allows managing a mapping of alias to the session id for having multiple
* active sessions at the same time.
* Allows managing a mapping of alias to the session id for having multiple active
* sessions at the same time.
*
* @author Rob Winch
* @since 1.0
@@ -33,7 +33,8 @@ public interface HttpSessionManager {
/**
* Gets the current session's alias from the {@link HttpServletRequest}.
*
* @param request the {@link HttpServletRequest} to obtain the current session's alias from.
* @param request the {@link HttpServletRequest} to obtain the current session's alias
* from.
* @return the current sessions' alias. Cannot be null.
*/
String getCurrentSessionAlias(HttpServletRequest request);
@@ -42,9 +43,10 @@ public interface HttpSessionManager {
* Gets a mapping of the session alias to the session id from the
* {@link HttpServletRequest}.
*
* @param request the {@link HttpServletRequest} to obtain the mapping from. Cannot be null.
* @param request the {@link HttpServletRequest} to obtain the mapping from. Cannot be
* null.
* @return a mapping of the session alias to the session id from the
* {@link HttpServletRequest}. Cannot be null.
* {@link HttpServletRequest}. Cannot be null.
*/
Map<String, String> getSessionIds(HttpServletRequest request);

View File

@@ -30,35 +30,50 @@ import org.springframework.session.Session;
public interface HttpSessionStrategy {
/**
* Obtains the requested session id from the provided {@link javax.servlet.http.HttpServletRequest}. For example,
* the session id might come from a cookie or a request header.
* Obtains the requested session id from the provided
* {@link javax.servlet.http.HttpServletRequest}. For example, the session id might
* come from a cookie or a request header.
*
* @param request the {@link javax.servlet.http.HttpServletRequest} to obtain the session id from. Cannot be null.
* @return the {@link javax.servlet.http.HttpServletRequest} to obtain the session id from.
* @param request the {@link javax.servlet.http.HttpServletRequest} to obtain the
* session id from. Cannot be null.
* @return the {@link javax.servlet.http.HttpServletRequest} to obtain the session id
* from.
*/
String getRequestedSessionId(HttpServletRequest request);
/**
* This method is invoked when a new session is created and should inform a client what the new session id is. For
* example, it might create a new cookie with the session id in it or set an HTTP response header with the value of
* the new session id.
* This method is invoked when a new session is created and should inform a client
* what the new session id is. For example, it might create a new cookie with the
* session id in it or set an HTTP response header with the value of the new session
* id.
*
* Some implementations may wish to associate additional information to the {@link Session} at this time. For example, they
* may wish to add the IP Address, browser headers, the username, etc to the {@link org.springframework.session.Session}.
* Some implementations may wish to associate additional information to the
* {@link Session} at this time. For example, they may wish to add the IP Address,
* browser headers, the username, etc to the
* {@link org.springframework.session.Session}.
*
* @param session the {@link org.springframework.session.Session} that is being sent to the client. Cannot be null.
* @param request the {@link javax.servlet.http.HttpServletRequest} that create the new {@link org.springframework.session.Session} Cannot be null.
* @param response the {@link javax.servlet.http.HttpServletResponse} that is associated with the {@link javax.servlet.http.HttpServletRequest} that created the new {@link org.springframework.session.Session} Cannot be null.
* @param session the {@link org.springframework.session.Session} that is being sent
* to the client. Cannot be null.
* @param request the {@link javax.servlet.http.HttpServletRequest} that create the
* new {@link org.springframework.session.Session} Cannot be null.
* @param response the {@link javax.servlet.http.HttpServletResponse} that is
* associated with the {@link javax.servlet.http.HttpServletRequest} that created the
* new {@link org.springframework.session.Session} Cannot be null.
*/
void onNewSession(Session session, HttpServletRequest request, HttpServletResponse response);
void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response);
/**
* This method is invoked when a session is invalidated and should inform a client that the session id is no longer valid. For
* example, it might remove a cookie with the session id in it or set an HTTP response header with an empty value indicating
* This method is invoked when a session is invalidated and should inform a client
* that the session id is no longer valid. For example, it might remove a cookie with
* the session id in it or set an HTTP response header with an empty value indicating
* to the client to no longer submit that session id.
*
* @param request the {@link javax.servlet.http.HttpServletRequest} that invalidated the {@link org.springframework.session.Session} Cannot be null.
* @param response the {@link javax.servlet.http.HttpServletResponse} that is associated with the {@link javax.servlet.http.HttpServletRequest} that invalidated the {@link org.springframework.session.Session} Cannot be null.
* @param request the {@link javax.servlet.http.HttpServletRequest} that invalidated
* the {@link org.springframework.session.Session} Cannot be null.
* @param response the {@link javax.servlet.http.HttpServletResponse} that is
* associated with the {@link javax.servlet.http.HttpServletRequest} that invalidated
* the {@link org.springframework.session.Session} Cannot be null.
*/
void onInvalidateSession(HttpServletRequest request, HttpServletResponse response);
}

View File

@@ -19,19 +19,18 @@ package org.springframework.session.web.http;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
* Some {@link HttpSessionStrategy} may also want to further customize
* {@link HttpServletRequest} and {@link HttpServletResponse} objects. For
* example, {@link CookieHttpSessionStrategy} customizes how URL rewriting is
* done to select which session should be used in the event multiple sessions
* are active.
* {@link HttpServletRequest} and {@link HttpServletResponse} objects. For example,
* {@link CookieHttpSessionStrategy} customizes how URL rewriting is done to select which
* session should be used in the event multiple sessions are active.
* </p>
*
* @author Rob Winch
* @since 1.0
* @see CookieHttpSessionStrategy
*/
public interface MultiHttpSessionStrategy extends HttpSessionStrategy, RequestResponsePostProcessor {
public interface MultiHttpSessionStrategy
extends HttpSessionStrategy, RequestResponsePostProcessor {
}

View File

@@ -28,8 +28,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Base class for response wrappers which encapsulate the logic for handling an event when the
* {@link javax.servlet.http.HttpServletResponse} is committed.
* Base class for response wrappers which encapsulate the logic for handling an event when
* the {@link javax.servlet.http.HttpServletResponse} is committed.
*
* @author Rob Winch
* @since 1.0
@@ -40,8 +40,9 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
private boolean disableOnCommitted;
/**
* The Content-Length response header. If this is greater than 0, then once {@link #contentWritten} is larger than
* or equal the response is considered committed.
* The Content-Length response header. If this is greater than 0, then once
* {@link #contentWritten} is larger than or equal the response is considered
* committed.
*/
private long contentLength;
@@ -78,22 +79,24 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Invoke this method to disable invoking {@link OnCommittedResponseWrapper#onResponseCommitted()} when the {@link javax.servlet.http.HttpServletResponse} is
* committed. This can be useful in the event that Async Web Requests are
* made.
* Invoke this method to disable invoking
* {@link OnCommittedResponseWrapper#onResponseCommitted()} when the
* {@link javax.servlet.http.HttpServletResponse} is committed. This can be useful in
* the event that Async Web Requests are made.
*/
public void disableOnResponseCommitted() {
this.disableOnCommitted = true;
}
/**
* Implement the logic for handling the {@link javax.servlet.http.HttpServletResponse} being committed.
* Implement the logic for handling the {@link javax.servlet.http.HttpServletResponse}
* being committed.
*/
protected abstract void onResponseCommitted();
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the
* superclass <code>sendError()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendError()</code>.
* @param sc the error status code
*/
@Override
@@ -103,8 +106,8 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the
* superclass <code>sendError()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendError()</code>.
* @param sc the error status code
*/
@Override
@@ -114,8 +117,8 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the
* superclass <code>sendRedirect()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendRedirect()</code>.
* @param location the redirect URL location
*/
@Override
@@ -125,8 +128,9 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the calling
* <code>getOutputStream().close()</code> or <code>getOutputStream().flush()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the calling <code>getOutputStream().close()</code> or
* <code>getOutputStream().flush()</code>.
* @throws IOException if an input or output exception occurred
*/
@Override
@@ -135,8 +139,9 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the
* <code>getWriter().close()</code> or <code>getWriter().flush()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the <code>getWriter().close()</code> or
* <code>getWriter().flush()</code>.
* @throws IOException if an input or output exception occurred
*/
@Override
@@ -145,8 +150,8 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the
* superclass <code>flushBuffer()</code>.
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>flushBuffer()</code>.
* @throws IOException if an input or output exception occurred
*/
@Override
@@ -196,14 +201,15 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Adds the contentLengthToWrite to the total contentWritten size and checks to see if the response should be
* written.
* Adds the contentLengthToWrite to the total contentWritten size and checks to see if
* the response should be written.
*
* @param contentLengthToWrite the size of the content that is about to be written.
*/
private void checkContentLength(long contentLengthToWrite) {
this.contentWritten += contentLengthToWrite;
boolean isBodyFullyWritten = this.contentLength > 0 && this.contentWritten >= this.contentLength;
boolean isBodyFullyWritten = this.contentLength > 0
&& this.contentWritten >= this.contentLength;
int bufferSize = getBufferSize();
boolean requiresFlush = bufferSize > 0 && this.contentWritten >= bufferSize;
if (isBodyFullyWritten || requiresFlush) {
@@ -226,9 +232,10 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Ensures {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling the prior to methods that commit the response. We delegate all methods
* to the original {@link java.io.PrintWriter} to ensure that the behavior is as close to the original {@link java.io.PrintWriter}
* as possible. See SEC-2039
* Ensures {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before
* calling the prior to methods that commit the response. We delegate all methods to
* the original {@link java.io.PrintWriter} to ensure that the behavior is as close to
* the original {@link java.io.PrintWriter} as possible. See SEC-2039
* @author Rob Winch
*/
private class SaveContextPrintWriter extends PrintWriter {
@@ -427,9 +434,10 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
/**
* Ensures{@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before calling methods that commit the response. We delegate all methods
* to the original {@link javax.servlet.ServletOutputStream} to ensure that the behavior is as close to the original {@link javax.servlet.ServletOutputStream}
* as possible. See SEC-2039
* Ensures{@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before
* calling methods that commit the response. We delegate all methods to the original
* {@link javax.servlet.ServletOutputStream} to ensure that the behavior is as close
* to the original {@link javax.servlet.ServletOutputStream} as possible. See SEC-2039
*
* @author Rob Winch
*/

View File

@@ -28,42 +28,45 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Allows for easily ensuring that a request is only invoked once per request. This is a simplified version of spring-web's
* OncePerRequestFilter and copied to reduce the foot print required to use the session support.
* Allows for easily ensuring that a request is only invoked once per request. This is a
* simplified version of spring-web's OncePerRequestFilter and copied to reduce the foot
* print required to use the session support.
*
* @author Rob Winch
* @since 1.0
*/
abstract class OncePerRequestFilter implements Filter {
/**
* Suffix that gets appended to the filter name for the
* "already filtered" request attribute.
* Suffix that gets appended to the filter name for the "already filtered" request
* attribute.
*/
public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
private String alreadyFilteredAttributeName = getClass().getName().concat(ALREADY_FILTERED_SUFFIX);
private String alreadyFilteredAttributeName = getClass().getName()
.concat(ALREADY_FILTERED_SUFFIX);
/**
* This {@code doFilter} implementation stores a request attribute for
* "already filtered", proceeding without filtering again if the
* attribute is already there.
* "already filtered", proceeding without filtering again if the attribute is already
* there.
* @param request the request
* @param response the response
* @param filterChain the filter chain
* @throws ServletException if request is not HTTP request
* @throws IOException in case of I/O operation exception
*/
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
public final void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
if (!(request instanceof HttpServletRequest)
|| !(response instanceof HttpServletResponse)) {
throw new ServletException(
"OncePerRequestFilter just supports HTTP requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
boolean hasAlreadyFilteredAttribute = request.getAttribute(this.alreadyFilteredAttributeName) != null;
boolean hasAlreadyFilteredAttribute = request
.getAttribute(this.alreadyFilteredAttributeName) != null;
if (hasAlreadyFilteredAttribute) {
@@ -84,9 +87,10 @@ abstract class OncePerRequestFilter implements Filter {
}
/**
* Same contract as for {@code doFilter}, but guaranteed to be
* just invoked once per request within a single request thread.
* <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the
* Same contract as for {@code doFilter}, but guaranteed to be just invoked once per
* request within a single request thread.
* <p>
* Provides HttpServletRequest and HttpServletResponse arguments instead of the
* default ServletRequest and ServletResponse ones.
*
* @param request the request
@@ -96,9 +100,9 @@ abstract class OncePerRequestFilter implements Filter {
* @throws IOException thrown when an I/O exception of some sort has occurred
* @see Filter#doFilter
*/
protected abstract void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException;
protected abstract void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException;
public void init(FilterConfig config) {
}

View File

@@ -31,13 +31,9 @@ public interface RequestResponsePostProcessor {
/**
* Allows customizing the {@link HttpServletRequest}.
*
* @param request
* the original {@link HttpServletRequest}. Cannot be null.
* @param response
* the original {@link HttpServletResponse}. This is NOT the
* result of
* {@link #wrapResponse(HttpServletRequest, HttpServletResponse)}
* Cannot be null. .
* @param request the original {@link HttpServletRequest}. Cannot be null.
* @param response the original {@link HttpServletResponse}. This is NOT the result of
* {@link #wrapResponse(HttpServletRequest, HttpServletResponse)} Cannot be null. .
* @return a non-null {@link HttpServletRequest}
*/
HttpServletRequest wrapRequest(HttpServletRequest request,
@@ -46,13 +42,9 @@ public interface RequestResponsePostProcessor {
/**
* Allows customizing the {@link HttpServletResponse}.
*
* @param request
* the original {@link HttpServletRequest}. This is NOT the
* result of
* {@link #wrapRequest(HttpServletRequest, HttpServletResponse)}.
* Cannot be null.
* @param response
* the original {@link HttpServletResponse}. Cannot be null.
* @param request the original {@link HttpServletRequest}. This is NOT the result of
* {@link #wrapRequest(HttpServletRequest, HttpServletResponse)}. Cannot be null.
* @param response the original {@link HttpServletResponse}. Cannot be null.
* @return a non-null {@link HttpServletResponse}
*/
HttpServletResponse wrapResponse(HttpServletRequest request,

View File

@@ -31,14 +31,15 @@ import org.springframework.session.events.SessionDestroyedEvent;
import org.springframework.web.context.ServletContextAware;
/**
* Receives {@link SessionDestroyedEvent} and {@link SessionCreatedEvent} and
* translates them into {@link HttpSessionEvent} and submits the
* {@link HttpSessionEvent} to every registered {@link HttpSessionListener}.
* Receives {@link SessionDestroyedEvent} and {@link SessionCreatedEvent} and translates
* them into {@link HttpSessionEvent} and submits the {@link HttpSessionEvent} to every
* registered {@link HttpSessionListener}.
*
* @author Rob Winch
* @since 1.1
*/
public class SessionEventHttpSessionListenerAdapter implements ApplicationListener<AbstractSessionEvent>, ServletContextAware {
public class SessionEventHttpSessionListenerAdapter
implements ApplicationListener<AbstractSessionEvent>, ServletContextAware {
private final List<HttpSessionListener> listeners;
private ServletContext context;
@@ -48,8 +49,11 @@ public class SessionEventHttpSessionListenerAdapter implements ApplicationListen
this.listeners = listeners;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
/*
* (non-Javadoc)
*
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.
* springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(AbstractSessionEvent event) {
if (this.listeners.isEmpty()) {
@@ -70,13 +74,18 @@ public class SessionEventHttpSessionListenerAdapter implements ApplicationListen
private HttpSessionEvent createHttpSessionEvent(AbstractSessionEvent event) {
ExpiringSession session = event.getSession();
HttpSession httpSession = new ExpiringSessionHttpSession<ExpiringSession>(session, this.context);
HttpSession httpSession = new ExpiringSessionHttpSession<ExpiringSession>(session,
this.context);
HttpSessionEvent httpSessionEvent = new HttpSessionEvent(httpSession);
return httpSessionEvent;
}
/* (non-Javadoc)
* @see org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet.ServletContext)
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet
* .ServletContext)
*/
public void setServletContext(ServletContext servletContext) {
this.context = servletContext;

View File

@@ -38,26 +38,35 @@ import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
/**
* Switches the {@link javax.servlet.http.HttpSession} implementation to be backed by a {@link org.springframework.session.Session}.
* Switches the {@link javax.servlet.http.HttpSession} implementation to be backed by a
* {@link org.springframework.session.Session}.
*
* The {@link SessionRepositoryFilter} wraps the {@link javax.servlet.http.HttpServletRequest} and overrides the methods
* to get an {@link javax.servlet.http.HttpSession} to be backed by a {@link org.springframework.session.Session} returned
* by the {@link org.springframework.session.SessionRepository}.
* The {@link SessionRepositoryFilter} wraps the
* {@link javax.servlet.http.HttpServletRequest} and overrides the methods to get an
* {@link javax.servlet.http.HttpSession} to be backed by a
* {@link org.springframework.session.Session} returned by the
* {@link org.springframework.session.SessionRepository}.
*
* The {@link SessionRepositoryFilter} uses a {@link HttpSessionStrategy} (default {@link CookieHttpSessionStrategy} to
* bridge logic between an {@link javax.servlet.http.HttpSession} and the {@link org.springframework.session.Session}
* abstraction. Specifically:
* The {@link SessionRepositoryFilter} uses a {@link HttpSessionStrategy} (default
* {@link CookieHttpSessionStrategy} to bridge logic between an
* {@link javax.servlet.http.HttpSession} and the
* {@link org.springframework.session.Session} abstraction. Specifically:
*
* <ul>
* <li>The session id is looked up using {@link HttpSessionStrategy#getRequestedSessionId(javax.servlet.http.HttpServletRequest)}.
* The default is to look in a cookie named SESSION.</li>
* <li>The session id of newly created {@link org.springframework.session.ExpiringSession} is sent to the client using
* <li>The client is notified that the session id is no longer valid with {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}</li>
* <li>The session id is looked up using
* {@link HttpSessionStrategy#getRequestedSessionId(javax.servlet.http.HttpServletRequest)}
* . The default is to look in a cookie named SESSION.</li>
* <li>The session id of newly created {@link org.springframework.session.ExpiringSession}
* is sent to the client using
* <li>The client is notified that the session id is no longer valid with
* {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
* </li>
* </ul>
*
* <p>
* The SessionRepositoryFilter must be placed before any Filter that access the HttpSession or that might commit the response
* to ensure the session is overridden and persisted properly.
* The SessionRepositoryFilter must be placed before any Filter that access the
* HttpSession or that might commit the response to ensure the session is overridden and
* persisted properly.
* </p>
*
* @param <S> the {@link ExpiringSession} type.
@@ -65,15 +74,18 @@ import org.springframework.session.SessionRepository;
* @author Rob Winch
*/
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter {
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class.getName().concat(".SESSION_LOGGER");
public class SessionRepositoryFilter<S extends ExpiringSession>
extends OncePerRequestFilter {
private static final String SESSION_LOGGER_NAME = SessionRepositoryFilter.class
.getName().concat(".SESSION_LOGGER");
private static final Log SESSION_LOGGER = LogFactory.getLog(SESSION_LOGGER_NAME);
/**
* The session repository request attribute name.
*/
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class.getName();
public static final String SESSION_REPOSITORY_ATTR = SessionRepository.class
.getName();
/**
* The default filter order.
@@ -99,7 +111,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
}
/**
* Sets the {@link HttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}.
* Sets the {@link HttpSessionStrategy} to be used. The default is a
* {@link CookieHttpSessionStrategy}.
*
* @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null.
*/
@@ -107,13 +120,16 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
if (httpSessionStrategy == null) {
throw new IllegalArgumentException("httpSessionStrategy cannot be null");
}
this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(httpSessionStrategy);
this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(
httpSessionStrategy);
}
/**
* Sets the {@link MultiHttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}.
* Sets the {@link MultiHttpSessionStrategy} to be used. The default is a
* {@link CookieHttpSessionStrategy}.
*
* @param httpSessionStrategy the {@link MultiHttpSessionStrategy} to use. Cannot be null.
* @param httpSessionStrategy the {@link MultiHttpSessionStrategy} to use. Cannot be
* null.
*/
public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {
if (httpSessionStrategy == null) {
@@ -122,14 +138,20 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
this.httpSessionStrategy = httpSessionStrategy;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, this.servletContext);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
HttpServletRequest strategyRequest = this.httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);
HttpServletResponse strategyResponse = this.httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);
HttpServletRequest strategyRequest = this.httpSessionStrategy
.wrapRequest(wrappedRequest, wrappedResponse);
HttpServletResponse strategyResponse = this.httpSessionStrategy
.wrapResponse(wrappedRequest, wrappedResponse);
try {
filterChain.doFilter(strategyRequest, strategyResponse);
@@ -149,7 +171,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
* @author Rob Winch
* @since 1.0
*/
private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {
private final class SessionRepositoryResponseWrapper
extends OnCommittedResponseWrapper {
private final SessionRepositoryRequestWrapper request;
@@ -158,7 +181,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
* @param request the request to be wrapped
* @param response the response to be wrapped
*/
SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {
SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,
HttpServletResponse response) {
super(response);
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
@@ -173,40 +197,48 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
}
/**
* A {@link javax.servlet.http.HttpServletRequest} that retrieves the {@link javax.servlet.http.HttpSession} using a
* A {@link javax.servlet.http.HttpServletRequest} that retrieves the
* {@link javax.servlet.http.HttpSession} using a
* {@link org.springframework.session.SessionRepository}.
*
* @author Rob Winch
* @since 1.0
*/
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
private final String CURRENT_SESSION_ATTR = HttpServletRequestWrapper.class.getName();
private final class SessionRepositoryRequestWrapper
extends HttpServletRequestWrapper {
private final String CURRENT_SESSION_ATTR = HttpServletRequestWrapper.class
.getName();
private Boolean requestedSessionIdValid;
private boolean requestedSessionInvalidated;
private final HttpServletResponse response;
private final ServletContext servletContext;
private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) {
private SessionRepositoryRequestWrapper(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext) {
super(request);
this.response = response;
this.servletContext = servletContext;
}
/**
* Uses the HttpSessionStrategy to write the session id tot he response and persist the Session.
* Uses the HttpSessionStrategy to write the session id tot he response and
* persist the Session.
*/
private void commitSession() {
HttpSessionWrapper wrappedSession = getCurrentSession();
if (wrappedSession == null) {
if (isInvalidateClientSession()) {
SessionRepositoryFilter.this.httpSessionStrategy.onInvalidateSession(this, this.response);
SessionRepositoryFilter.this.httpSessionStrategy
.onInvalidateSession(this, this.response);
}
}
else {
S session = wrappedSession.getSession();
SessionRepositoryFilter.this.sessionRepository.save(session);
if (!isRequestedSessionIdValid() || !session.getId().equals(getRequestedSessionId())) {
SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session, this, this.response);
if (!isRequestedSessionIdValid()
|| !session.getId().equals(getRequestedSessionId())) {
SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
this, this.response);
}
}
}
@@ -230,7 +262,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
HttpSession session = getSession(false);
if (session == null) {
throw new IllegalStateException("Cannot change session ID. There is no session associated with this request.");
throw new IllegalStateException(
"Cannot change session ID. There is no session associated with this request.");
}
// eagerly get session attributes in case implementation lazily loads them
@@ -281,7 +314,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
}
private S getSession(String sessionId) {
S session = SessionRepositoryFilter.this.sessionRepository.getSession(sessionId);
S session = SessionRepositoryFilter.this.sessionRepository
.getSession(sessionId);
if (session == null) {
return null;
}
@@ -310,9 +344,11 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
return null;
}
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER
.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
+ SESSION_LOGGER_NAME, new RuntimeException("For debugging purposes only (not an error)"));
SESSION_LOGGER.debug(
"A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
+ SESSION_LOGGER_NAME,
new RuntimeException(
"For debugging purposes only (not an error)"));
}
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
session.setLastAccessedTime(System.currentTimeMillis());
@@ -336,7 +372,8 @@ public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerR
@Override
public String getRequestedSessionId() {
return SessionRepositoryFilter.this.httpSessionStrategy.getRequestedSessionId(this);
return SessionRepositoryFilter.this.httpSessionStrategy
.getRequestedSessionId(this);
}
/**

View File

@@ -42,13 +42,14 @@ 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 that Web Socket Sessions are destroyed when a {@link Session} is
* terminated</li>
* <li>Ensures the 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>
*
* <p>Example usage</p>
* <p>
* Example usage
* </p>
*
* <code>
* {@literal @Configuration}
@@ -70,12 +71,12 @@ import org.springframework.web.util.UrlPathHelper;
* }
* </code>
*
* @param <S>
* the type of ExpiringSession
* @param <S> the type of ExpiringSession
* @author Rob Winch
* @since 1.0
*/
public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends ExpiringSession> extends AbstractWebSocketMessageBrokerConfigurer {
public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends ExpiringSession>
extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
@SuppressWarnings("rawtypes")
@@ -92,26 +93,23 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
public final void registerStompEndpoints(StompEndpointRegistry registry) {
if (registry instanceof WebMvcStompEndpointRegistry) {
WebMvcStompEndpointRegistry mvcRegistry = (WebMvcStompEndpointRegistry) registry;
configureStompEndpoints(new SessionStompEndpointRegistry(mvcRegistry, sessionRepositoryInterceptor()));
configureStompEndpoints(new SessionStompEndpointRegistry(mvcRegistry,
sessionRepositoryInterceptor()));
}
}
/**
* Register STOMP endpoints mapping each to a specific URL and (optionally)
* enabling and configuring SockJS fallback options with a
* {@link SessionRepositoryMessageInterceptor} automatically added as an
* interceptor.
* Register STOMP endpoints mapping each to a specific URL and (optionally) enabling
* and configuring SockJS fallback options with a
* {@link SessionRepositoryMessageInterceptor} automatically added as an interceptor.
*
* @param registry
* the {@link StompEndpointRegistry} which automatically has a
* {@link SessionRepositoryMessageInterceptor} added to it.
* @param registry the {@link StompEndpointRegistry} which automatically has a
* {@link SessionRepositoryMessageInterceptor} added to it.
*/
protected abstract void configureStompEndpoints(StompEndpointRegistry registry);
@Override
public void configureWebSocketTransport(
WebSocketTransportRegistration registration) {
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(wsConnectHandlerDecoratorFactory());
}
@@ -145,7 +143,8 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
}
public StompWebSocketEndpointRegistration addEndpoint(String... paths) {
StompWebSocketEndpointRegistration endpoints = this.registry.addEndpoint(paths);
StompWebSocketEndpointRegistration endpoints = this.registry
.addEndpoint(paths);
endpoints.addInterceptors(this.interceptor);
return endpoints;
}
@@ -158,7 +157,8 @@ public abstract class AbstractSessionWebSocketMessageBrokerConfigurer<S extends
this.registry.setUrlPathHelper(urlPathHelper);
}
public WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler errorHandler) {
public WebMvcStompEndpointRegistry setErrorHandler(
StompSubProtocolErrorHandler errorHandler) {
return this.registry.setErrorHandler(errorHandler);
}
}

View File

@@ -22,9 +22,8 @@ import org.springframework.session.web.socket.handler.WebSocketRegistryListener;
import org.springframework.web.socket.WebSocketSession;
/**
* Similar to Spring
* {@link org.springframework.web.socket.messaging.SessionConnectEvent} except
* that it provides access to the {@link WebSocketSession} to allow mapping the
* Similar to Spring {@link org.springframework.web.socket.messaging.SessionConnectEvent}
* except that it provides access to the {@link WebSocketSession} to allow mapping the
* Spring Session to the {@link WebSocketSession}.
*
* @author Rob Winch

View File

@@ -31,20 +31,21 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
/**
* Ensures that a {@link SessionConnectEvent} is published in
* {@link WebSocketHandler#afterConnectionEstablished(WebSocketSession)}. This
* is necessary so that the {@link WebSocketSession} can be mapped to the
* corresponding Spring {@link Session} to terminate any
* {@link WebSocketSession} associated with a Spring {@link Session} that was
* destroyed.
* {@link WebSocketHandler#afterConnectionEstablished(WebSocketSession)}. This is
* necessary so that the {@link WebSocketSession} can be mapped to the corresponding
* Spring {@link Session} to terminate any {@link WebSocketSession} associated with a
* Spring {@link Session} that was destroyed.
*
* @author Rob Winch
* @since 1.0
*
* @see WebSocketRegistryListener
*/
public final class WebSocketConnectHandlerDecoratorFactory implements WebSocketHandlerDecoratorFactory {
public final class WebSocketConnectHandlerDecoratorFactory
implements WebSocketHandlerDecoratorFactory {
private static final Log logger = LogFactory.getLog(WebSocketConnectHandlerDecoratorFactory.class);
private static final Log logger = LogFactory
.getLog(WebSocketConnectHandlerDecoratorFactory.class);
private final ApplicationEventPublisher eventPublisher;
@@ -79,7 +80,8 @@ public final class WebSocketConnectHandlerDecoratorFactory implements WebSocketH
private void publishEvent(ApplicationEvent event) {
try {
WebSocketConnectHandlerDecoratorFactory.this.eventPublisher.publishEvent(event);
WebSocketConnectHandlerDecoratorFactory.this.eventPublisher
.publishEvent(event);
}
catch (Throwable ex) {
logger.error("Error publishing " + event + ".", ex);

View File

@@ -36,8 +36,8 @@ import org.springframework.web.socket.messaging.SessionDisconnectEvent;
/**
* <p>
* Keeps track of mapping the Spring Session ID to the {@link WebSocketSession}
* and ensuring when a {@link SessionDestroyedEvent} is fired that the
* Keeps track of mapping the Spring Session ID to the {@link WebSocketSession} and
* ensuring when a {@link SessionDestroyedEvent} is fired that the
* {@link WebSocketSession} is closed.
* </p>
*
@@ -46,11 +46,13 @@ import org.springframework.web.socket.messaging.SessionDisconnectEvent;
* @author Mark Anderson
* @since 1.0
*/
public final class WebSocketRegistryListener implements ApplicationListener<ApplicationEvent> {
public final class WebSocketRegistryListener
implements ApplicationListener<ApplicationEvent> {
private static final Log logger = LogFactory.getLog(WebSocketRegistryListener.class);
static final CloseStatus SESSION_EXPIRED_STATUS = new CloseStatus(CloseStatus.POLICY_VIOLATION.getCode(),
static final CloseStatus SESSION_EXPIRED_STATUS = new CloseStatus(
CloseStatus.POLICY_VIOLATION.getCode(),
"This connection was established under an authenticated HTTP Session that has expired");
private final ConcurrentHashMap<String, Map<String, WebSocketSession>> httpSessionIdToWsSessions = new ConcurrentHashMap<String, Map<String, WebSocketSession>>();
@@ -66,8 +68,10 @@ public final class WebSocketRegistryListener implements ApplicationListener<Appl
}
else if (event instanceof SessionDisconnectEvent) {
SessionDisconnectEvent e = (SessionDisconnectEvent) event;
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor.getSessionAttributes(e.getMessage().getHeaders());
String httpSessionId = sessionAttributes == null ? null : SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes);
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor
.getSessionAttributes(e.getMessage().getHeaders());
String httpSessionId = sessionAttributes == null ? null
: SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes);
afterConnectionClosed(httpSessionId, e.getSessionId());
}
}
@@ -92,7 +96,8 @@ public final class WebSocketRegistryListener implements ApplicationListener<Appl
return;
}
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions.get(httpSessionId);
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions
.get(httpSessionId);
if (sessions != null) {
boolean result = sessions.remove(wsSessionId) != null;
if (logger.isDebugEnabled()) {
@@ -101,17 +106,18 @@ public final class WebSocketRegistryListener implements ApplicationListener<Appl
if (sessions.isEmpty()) {
this.httpSessionIdToWsSessions.remove(httpSessionId);
if (logger.isDebugEnabled()) {
logger.debug("Removed the corresponding HTTP Session for " + wsSessionId + " since it contained no WebSocket mappings");
logger.debug("Removed the corresponding HTTP Session for "
+ wsSessionId + " since it contained no WebSocket mappings");
}
}
}
}
private void registerWsSession(String httpSessionId, WebSocketSession wsSession) {
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions.get(httpSessionId);
Map<String, WebSocketSession> sessions = this.httpSessionIdToWsSessions
.get(httpSessionId);
if (sessions == null) {
sessions =
new ConcurrentHashMap<String, WebSocketSession>();
sessions = new ConcurrentHashMap<String, WebSocketSession>();
this.httpSessionIdToWsSessions.putIfAbsent(httpSessionId, sessions);
sessions = this.httpSessionIdToWsSessions.get(httpSessionId);
}
@@ -119,19 +125,24 @@ public final class WebSocketRegistryListener implements ApplicationListener<Appl
}
private void closeWsSessions(String httpSessionId) {
Map<String, WebSocketSession> sessionsToClose = this.httpSessionIdToWsSessions.remove(httpSessionId);
Map<String, WebSocketSession> sessionsToClose = this.httpSessionIdToWsSessions
.remove(httpSessionId);
if (sessionsToClose == null) {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Closing WebSocket connections associated to expired HTTP Session " + httpSessionId);
logger.debug(
"Closing WebSocket connections associated to expired HTTP Session "
+ httpSessionId);
}
for (WebSocketSession toClose : sessionsToClose.values()) {
try {
toClose.close(SESSION_EXPIRED_STATUS);
}
catch (IOException e) {
logger.debug("Failed to close WebSocketSession (this is nothing to worry about but for debugging only)", e);
logger.debug(
"Failed to close WebSocketSession (this is nothing to worry about but for debugging only)",
e);
}
}
}

View File

@@ -40,35 +40,30 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
/**
* <p>
* Acts as a {@link ChannelInterceptor} and a {@link HandshakeInterceptor} to
* ensure the {@link ExpiringSession#getLastAccessedTime()} is up to date.
* Acts as a {@link ChannelInterceptor} and a {@link HandshakeInterceptor} to ensure the
* {@link ExpiringSession#getLastAccessedTime()} is up to date.
* </p>
* <ul>
* <li>
* Associates the {@link Session#getId()} with the WebSocket Session
* attributes when the handshake is performed. This is later used when
* intercepting messages to ensure the
* {@link ExpiringSession#getLastAccessedTime()} is updated.
* </li>
* <li>
* Intercepts {@link Message}'s that are have {@link SimpMessageType} that
* corresponds to {@link #setMatchingMessageTypes(Set)} and updates the last
* accessed time of the {@link Session}. If the {@link Session} is expired, the
* {@link Message} is prevented from proceeding.</li>
* <li>Associates the {@link Session#getId()} with the WebSocket Session attributes when
* the handshake is performed. This is later used when intercepting messages to ensure the
* {@link ExpiringSession#getLastAccessedTime()} is updated.</li>
* <li>Intercepts {@link Message}'s that are have {@link SimpMessageType} that corresponds
* to {@link #setMatchingMessageTypes(Set)} and updates the last accessed time of the
* {@link Session}. If the {@link Session} is expired, the {@link Message} is prevented
* from proceeding.</li>
* </ul>
*
* <p>
* In order to work {@link SessionRepositoryMessageInterceptor} must be
* registered as a {@link ChannelInterceptor} and a {@link HandshakeInterceptor}
* .
* In order to work {@link SessionRepositoryMessageInterceptor} must be registered as a
* {@link ChannelInterceptor} and a {@link HandshakeInterceptor} .
* </p>
*
* @param <S> the {@link ExpiringSession} type
* @author Rob Winch
* @since 1.0
*/
public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession> extends ChannelInterceptorAdapter
implements HandshakeInterceptor {
public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession>
extends ChannelInterceptorAdapter implements HandshakeInterceptor {
private static final String SPRING_SESSION_ID_ATTR_NAME = "SPRING.SESSION.ID";
@@ -84,15 +79,16 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
public SessionRepositoryMessageInterceptor(SessionRepository<S> sessionRepository) {
Assert.notNull(sessionRepository, "sessionRepository cannot be null");
this.sessionRepository = sessionRepository;
this.matchingMessageTypes = EnumSet.of(SimpMessageType.CONNECT, SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE, SimpMessageType.UNSUBSCRIBE);
this.matchingMessageTypes = EnumSet.of(SimpMessageType.CONNECT,
SimpMessageType.MESSAGE, SimpMessageType.SUBSCRIBE,
SimpMessageType.UNSUBSCRIBE);
}
/**
* <p>
* Sets the {@link SimpMessageType} to match on. If the {@link Message}
* matches, then {@link #preSend(Message, MessageChannel)} ensures the
* {@link Session} is not expired and updates the
* {@link ExpiringSession#getLastAccessedTime()}
* Sets the {@link SimpMessageType} to match on. If the {@link Message} matches, then
* {@link #preSend(Message, MessageChannel)} ensures the {@link Session} is not
* expired and updates the {@link ExpiringSession#getLastAccessedTime()}
* </p>
*
* <p>
@@ -100,14 +96,13 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
* SimpMessageType.SUBSCRIBE, SimpMessageType.UNSUBSCRIBE.
* </p>
*
* @param matchingMessageTypes
* the {@link SimpMessageType} to match on in
* {@link #preSend(Message, MessageChannel)}, else the
* {@link Message} is continued without accessing or updating the
* {@link Session}
* @param matchingMessageTypes the {@link SimpMessageType} to match on in
* {@link #preSend(Message, MessageChannel)}, else the {@link Message} is continued
* without accessing or updating the {@link Session}
*/
public void setMatchingMessageTypes(Set<SimpMessageType> matchingMessageTypes) {
Assert.notEmpty(matchingMessageTypes, "matchingMessageTypes cannot be null or empty");
Assert.notEmpty(matchingMessageTypes,
"matchingMessageTypes cannot be null or empty");
this.matchingMessageTypes = matchingMessageTypes;
}
@@ -116,12 +111,15 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
if (message == null) {
return message;
}
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(message.getHeaders());
SimpMessageType messageType = SimpMessageHeaderAccessor
.getMessageType(message.getHeaders());
if (!this.matchingMessageTypes.contains(messageType)) {
return super.preSend(message, channel);
}
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor.getSessionAttributes(message.getHeaders());
String sessionId = sessionHeaders == null ? null : (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME);
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor
.getSessionAttributes(message.getHeaders());
String sessionId = sessionHeaders == null ? null
: (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME);
if (sessionId != null) {
S session = this.sessionRepository.getSession(sessionId);
if (session != null) {
@@ -133,9 +131,8 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
return super.preSend(message, channel);
}
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
@@ -146,9 +143,8 @@ public final class SessionRepositoryMessageInterceptor<S extends ExpiringSession
return true;
}
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
public static String getSessionId(Map<String, Object> attributes) {

View File

@@ -37,7 +37,8 @@ public class MapSessionRepositoryTests {
@Test
public void getSessionExpired() {
this.session.setMaxInactiveIntervalInSeconds(1);
this.session.setLastAccessedTime(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
this.session.setLastAccessedTime(
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
this.repository.save(this.session);
assertThat(this.repository.getSession(this.session.getId())).isNull();
@@ -48,16 +49,19 @@ public class MapSessionRepositoryTests {
ExpiringSession session = this.repository.createSession();
assertThat(session).isInstanceOf(MapSession.class);
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
}
@Test
public void createSessionCustomDefaultExpiration() {
final int expectedMaxInterval = new MapSession().getMaxInactiveIntervalInSeconds() + 10;
final int expectedMaxInterval = new MapSession().getMaxInactiveIntervalInSeconds()
+ 10;
this.repository.setDefaultMaxInactiveInterval(expectedMaxInterval);
ExpiringSession session = this.repository.createSession();
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(expectedMaxInterval);
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(expectedMaxInterval);
}
}

View File

@@ -78,7 +78,8 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
@Test
public void usesReadSessionIds() throws Exception {
String sessionId = "sessionId";
given(this.cookieSerializer.readCookieValues(any(HttpServletRequest.class))).willReturn(Arrays.asList(sessionId));
given(this.cookieSerializer.readCookieValues(any(HttpServletRequest.class)))
.willReturn(Arrays.asList(sessionId));
this.sessionRepositoryFilter.doFilter(this.request, this.response, this.chain);
@@ -87,15 +88,16 @@ public class EnableSpringHttpSessionCustomCookieSerializerTests {
@Test
public void usesWrite() throws Exception {
this.sessionRepositoryFilter.doFilter(this.request, this.response, new MockFilterChain() {
this.sessionRepositoryFilter.doFilter(this.request, this.response,
new MockFilterChain() {
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
((HttpServletRequest) request).getSession();
super.doFilter(request, response);
}
});
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
((HttpServletRequest) request).getSession();
super.doFilter(request, response);
}
});
verify(this.cookieSerializer).writeCookieValue(any(CookieValue.class));
}

View File

@@ -70,13 +70,17 @@ public class EnableSpringHttpSessionCustomMultiHttpSessionStrategyTests {
@Test
public void wrapRequestAndResponseUsed() throws Exception {
given(this.strategy.wrapRequest(any(HttpServletRequest.class), any(HttpServletResponse.class))).willReturn(this.request);
given(this.strategy.wrapResponse(any(HttpServletRequest.class), any(HttpServletResponse.class))).willReturn(this.response);
given(this.strategy.wrapRequest(any(HttpServletRequest.class),
any(HttpServletResponse.class))).willReturn(this.request);
given(this.strategy.wrapResponse(any(HttpServletRequest.class),
any(HttpServletResponse.class))).willReturn(this.response);
this.sessionRepositoryFilter.doFilter(this.request, this.response, this.chain);
verify(this.strategy).wrapRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
verify(this.strategy).wrapResponse(any(HttpServletRequest.class), any(HttpServletResponse.class));
verify(this.strategy).wrapRequest(any(HttpServletRequest.class),
any(HttpServletResponse.class));
verify(this.strategy).wrapResponse(any(HttpServletRequest.class),
any(HttpServletResponse.class));
}
@EnableSpringHttpSession

View File

@@ -55,8 +55,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* The GemFireOperationsSessionRepositoryTest class is a test suite of test cases testing the contract and functionality
* of the GemFireOperationsSessionRepository class.
* The GemFireOperationsSessionRepositoryTest class is a test suite of test cases testing
* the contract and functionality of the GemFireOperationsSessionRepository class.
*
* @author John Blum
* @since 1.1.0
@@ -89,23 +89,32 @@ public class GemFireOperationsSessionRepositoryTest {
@Before
public void setup() throws Exception {
given(this.mockRegion.getAttributesMutator()).willReturn(this.mockAttributesMutator);
given(this.mockRegion.getAttributesMutator())
.willReturn(this.mockAttributesMutator);
given(this.mockRegion.getFullPath()).willReturn("/Example");
given(this.mockTemplate.<Object, ExpiringSession>getRegion()).willReturn(this.mockRegion);
given(this.mockTemplate.<Object, ExpiringSession>getRegion())
.willReturn(this.mockRegion);
this.sessionRepository = new GemFireOperationsSessionRepository(this.mockTemplate);
this.sessionRepository.setApplicationEventPublisher(this.mockApplicationEventPublisher);
this.sessionRepository.setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.sessionRepository = new GemFireOperationsSessionRepository(
this.mockTemplate);
this.sessionRepository
.setApplicationEventPublisher(this.mockApplicationEventPublisher);
this.sessionRepository
.setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.sessionRepository.afterPropertiesSet();
assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(this.mockApplicationEventPublisher);
assertThat(this.sessionRepository.getFullyQualifiedRegionName()).isEqualTo("/Example");
assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(this.sessionRepository.getApplicationEventPublisher())
.isSameAs(this.mockApplicationEventPublisher);
assertThat(this.sessionRepository.getFullyQualifiedRegionName())
.isEqualTo("/Example");
assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@After
public void tearDown() {
verify(this.mockAttributesMutator, times(1)).addCacheListener(same(this.sessionRepository));
verify(this.mockAttributesMutator, times(1))
.addCacheListener(same(this.sessionRepository));
verify(this.mockRegion, times(1)).getFullPath();
verify(this.mockTemplate, times(1)).getRegion();
}
@@ -119,17 +128,21 @@ public class GemFireOperationsSessionRepositoryTest {
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
given(mockSelectResults.asList()).willReturn(Collections.<Object>singletonList(mockSession));
given(mockSelectResults.asList())
.willReturn(Collections.<Object>singletonList(mockSession));
String indexName = "vip";
String indexValue = "rwinch";
String expectedQql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
String expectedQql = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
this.sessionRepository.getFullyQualifiedRegionName(), indexName);
given(this.mockTemplate.find(eq(expectedQql), eq(indexValue))).willReturn(mockSelectResults);
given(this.mockTemplate.find(eq(expectedQql), eq(indexValue)))
.willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = this.sessionRepository.findByIndexNameAndIndexValue(indexName, indexValue);
Map<String, ExpiringSession> sessions = this.sessionRepository
.findByIndexNameAndIndexValue(indexName, indexValue);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(1);
@@ -145,7 +158,8 @@ public class GemFireOperationsSessionRepositoryTest {
public void findByPrincipalNameFindsMatchingSessions() throws Exception {
ExpiringSession mockSessionOne = mock(ExpiringSession.class, "MockSessionOne");
ExpiringSession mockSessionTwo = mock(ExpiringSession.class, "MockSessionTwo");
ExpiringSession mockSessionThree = mock(ExpiringSession.class, "MockSessionThree");
ExpiringSession mockSessionThree = mock(ExpiringSession.class,
"MockSessionThree");
given(mockSessionOne.getId()).willReturn("1");
given(mockSessionTwo.getId()).willReturn("2");
@@ -153,17 +167,22 @@ public class GemFireOperationsSessionRepositoryTest {
SelectResults<Object> mockSelectResults = mock(SelectResults.class);
given(mockSelectResults.asList()).willReturn(Arrays.<Object>asList(mockSessionOne, mockSessionTwo, mockSessionThree));
given(mockSelectResults.asList()).willReturn(
Arrays.<Object>asList(mockSessionOne, mockSessionTwo, mockSessionThree));
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
String expectedOql = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
given(this.mockTemplate.find(eq(expectedOql), eq(principalName))).willReturn(mockSelectResults);
given(this.mockTemplate.find(eq(expectedOql), eq(principalName)))
.willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
Map<String, ExpiringSession> sessions = this.sessionRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.size()).isEqualTo(3);
@@ -187,13 +206,17 @@ public class GemFireOperationsSessionRepositoryTest {
String principalName = "jblum";
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
String expectedOql = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
given(this.mockTemplate.find(eq(expectedOql), eq(principalName))).willReturn(mockSelectResults);
given(this.mockTemplate.find(eq(expectedOql), eq(principalName)))
.willReturn(mockSelectResults);
Map<String, ExpiringSession> sessions = this.sessionRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
Map<String, ExpiringSession> sessions = this.sessionRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principalName);
assertThat(sessions).isNotNull();
assertThat(sessions.isEmpty()).isTrue();
@@ -204,8 +227,10 @@ public class GemFireOperationsSessionRepositoryTest {
@Test
public void prepareQueryReturnsPrincipalNameOql() {
String actualQql = this.sessionRepository.prepareQuery(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
String actualQql = this.sessionRepository
.prepareQuery(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
String expectedOql = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY,
this.sessionRepository.getFullyQualifiedRegionName());
assertThat(actualQql).isEqualTo(expectedOql);
@@ -215,7 +240,8 @@ public class GemFireOperationsSessionRepositoryTest {
public void prepareQueryReturnsIndexNameValueOql() {
String attributeName = "testAttributeName";
String actualOql = this.sessionRepository.prepareQuery(attributeName);
String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
String expectedOql = String.format(
GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_VALUE_QUERY,
this.sessionRepository.getFullyQualifiedRegionName(), attributeName);
assertThat(actualOql).isEqualTo(expectedOql);
@@ -227,12 +253,16 @@ public class GemFireOperationsSessionRepositoryTest {
ExpiringSession session = this.sessionRepository.createSession();
assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(session).isInstanceOf(
AbstractGemFireOperationsSessionRepository.GemFireSession.class);
assertThat(session.getId()).isNotNull();
assertThat(session.getAttributeNames().isEmpty()).isTrue();
assertThat(session.getCreationTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getLastAccessedTime()).isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(session.getCreationTime())
.isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getLastAccessedTime())
.isGreaterThanOrEqualTo(beforeOrAtCreationTime);
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
}
@Test
@@ -248,19 +278,22 @@ public class GemFireOperationsSessionRepositoryTest {
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgumentAt(0, ApplicationEvent.class);
ApplicationEvent applicationEvent = invocation.getArgumentAt(0,
ApplicationEvent.class);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource()).isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSource()).isSameAs(
GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSession()).isSameAs(mockSession);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(any(ApplicationEvent.class));
}).given(this.mockApplicationEventPublisher)
.publishEvent(any(ApplicationEvent.class));
assertThat(this.sessionRepository.getSession(expectedSessionId)).isNull();
@@ -268,7 +301,8 @@ public class GemFireOperationsSessionRepositoryTest {
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(mockSession, times(1)).isExpired();
verify(mockSession, times(2)).getId();
verify(this.mockApplicationEventPublisher, times(1)).publishEvent(isA(SessionDeletedEvent.class));
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
@Test
@@ -276,7 +310,8 @@ public class GemFireOperationsSessionRepositoryTest {
final String expectedId = "1";
final long expectedCreationTime = System.currentTimeMillis();
final long currentLastAccessedTime = (expectedCreationTime + TimeUnit.MINUTES.toMillis(5));
final long currentLastAccessedTime = (expectedCreationTime
+ TimeUnit.MINUTES.toMillis(5));
ExpiringSession mockSession = mock(ExpiringSession.class);
@@ -284,7 +319,8 @@ public class GemFireOperationsSessionRepositoryTest {
given(mockSession.getId()).willReturn(expectedId);
given(mockSession.getCreationTime()).willReturn(expectedCreationTime);
given(mockSession.getLastAccessedTime()).willReturn(currentLastAccessedTime);
given(mockSession.getAttributeNames()).willReturn(Collections.singleton("attrOne"));
given(mockSession.getAttributeNames())
.willReturn(Collections.singleton("attrOne"));
given(mockSession.getAttribute(eq("attrOne"))).willReturn("test");
given(this.mockTemplate.get(eq(expectedId))).willReturn(mockSession);
@@ -293,10 +329,14 @@ public class GemFireOperationsSessionRepositoryTest {
assertThat(actualSession).isNotSameAs(mockSession);
assertThat(actualSession.getId()).isEqualTo(expectedId);
assertThat(actualSession.getCreationTime()).isEqualTo(expectedCreationTime);
assertThat(actualSession.getLastAccessedTime()).isNotEqualTo(currentLastAccessedTime);
assertThat(actualSession.getLastAccessedTime()).isGreaterThanOrEqualTo(expectedCreationTime);
assertThat(actualSession.getAttributeNames()).isEqualTo(Collections.singleton("attrOne"));
assertThat(String.valueOf(actualSession.getAttribute("attrOne"))).isEqualTo("test");
assertThat(actualSession.getLastAccessedTime())
.isNotEqualTo(currentLastAccessedTime);
assertThat(actualSession.getLastAccessedTime())
.isGreaterThanOrEqualTo(expectedCreationTime);
assertThat(actualSession.getAttributeNames())
.isEqualTo(Collections.singleton("attrOne"));
assertThat(String.valueOf(actualSession.getAttribute("attrOne")))
.isEqualTo("test");
verify(this.mockTemplate, times(1)).get(eq(expectedId));
verify(mockSession, times(1)).isExpired();
@@ -318,31 +358,40 @@ public class GemFireOperationsSessionRepositoryTest {
final String expectedSessionId = "1";
final long expectedCreationTime = System.currentTimeMillis();
final long expectedLastAccessTime = (expectedCreationTime + TimeUnit.MINUTES.toMillis(5));
final long expectedLastAccessTime = (expectedCreationTime
+ TimeUnit.MINUTES.toMillis(5));
ExpiringSession mockSession = mock(ExpiringSession.class);
given(mockSession.getId()).willReturn(expectedSessionId);
given(mockSession.getCreationTime()).willReturn(expectedCreationTime);
given(mockSession.getLastAccessedTime()).willReturn(expectedLastAccessTime);
given(mockSession.getMaxInactiveIntervalInSeconds()).willReturn(MAX_INACTIVE_INTERVAL_IN_SECONDS);
given(mockSession.getMaxInactiveIntervalInSeconds())
.willReturn(MAX_INACTIVE_INTERVAL_IN_SECONDS);
given(mockSession.getAttributeNames()).willReturn(Collections.<String>emptySet());
given(this.mockTemplate.put(eq(expectedSessionId), isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class)))
.willAnswer(new Answer<ExpiringSession>() {
public ExpiringSession answer(final InvocationOnMock invocation) throws Throwable {
ExpiringSession session = invocation.getArgumentAt(1, ExpiringSession.class);
given(this.mockTemplate.put(eq(expectedSessionId),
isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class)))
.willAnswer(new Answer<ExpiringSession>() {
public ExpiringSession answer(
final InvocationOnMock invocation) throws Throwable {
ExpiringSession session = invocation.getArgumentAt(1,
ExpiringSession.class);
assertThat(session).isNotNull();
assertThat(session.getId()).isEqualTo(expectedSessionId);
assertThat(session.getCreationTime()).isEqualTo(expectedCreationTime);
assertThat(session.getLastAccessedTime()).isEqualTo(expectedLastAccessTime);
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(session.getAttributeNames().isEmpty()).isTrue();
assertThat(session).isNotNull();
assertThat(session.getId()).isEqualTo(expectedSessionId);
assertThat(session.getCreationTime())
.isEqualTo(expectedCreationTime);
assertThat(session.getLastAccessedTime())
.isEqualTo(expectedLastAccessTime);
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
assertThat(session.getAttributeNames().isEmpty())
.isTrue();
return null;
}
});
return null;
}
});
this.sessionRepository.save(mockSession);
@@ -351,7 +400,8 @@ public class GemFireOperationsSessionRepositoryTest {
verify(mockSession, times(1)).getLastAccessedTime();
verify(mockSession, times(1)).getMaxInactiveIntervalInSeconds();
verify(mockSession, times(1)).getAttributeNames();
verify(this.mockTemplate, times(1)).put(eq(expectedSessionId), isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class));
verify(this.mockTemplate, times(1)).put(eq(expectedSessionId),
isA(AbstractGemFireOperationsSessionRepository.GemFireSession.class));
}
@Test
@@ -365,25 +415,29 @@ public class GemFireOperationsSessionRepositoryTest {
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgumentAt(0, ApplicationEvent.class);
ApplicationEvent applicationEvent = invocation.getArgumentAt(0,
ApplicationEvent.class);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource()).isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSource()).isSameAs(
GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSession()).isSameAs(mockSession);
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(isA(SessionDeletedEvent.class));
}).given(this.mockApplicationEventPublisher)
.publishEvent(isA(SessionDeletedEvent.class));
this.sessionRepository.delete(expectedSessionId);
verify(mockSession, times(1)).getId();
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(this.mockApplicationEventPublisher, times(1)).publishEvent(isA(SessionDeletedEvent.class));
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
@Test
@@ -394,27 +448,32 @@ public class GemFireOperationsSessionRepositoryTest {
willAnswer(new Answer<Void>() {
public Void answer(final InvocationOnMock invocation) throws Throwable {
ApplicationEvent applicationEvent = invocation.getArgumentAt(0, ApplicationEvent.class);
ApplicationEvent applicationEvent = invocation.getArgumentAt(0,
ApplicationEvent.class);
assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class);
AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent;
assertThat(sessionEvent.getSource()).isSameAs(GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSource()).isSameAs(
GemFireOperationsSessionRepositoryTest.this.sessionRepository);
assertThat(sessionEvent.getSession()).isNull();
assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId);
return null;
}
}).given(this.mockApplicationEventPublisher).publishEvent(isA(SessionDeletedEvent.class));
}).given(this.mockApplicationEventPublisher)
.publishEvent(isA(SessionDeletedEvent.class));
this.sessionRepository.delete(expectedSessionId);
verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId));
verify(this.mockApplicationEventPublisher, times(1)).publishEvent(isA(SessionDeletedEvent.class));
verify(this.mockApplicationEventPublisher, times(1))
.publishEvent(isA(SessionDeletedEvent.class));
}
protected abstract class GemfireOperationsAccessor extends GemfireAccessor implements GemfireOperations {
protected abstract class GemfireOperationsAccessor extends GemfireAccessor
implements GemfireOperations {
}
}

View File

@@ -41,8 +41,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* The GemFireHttpSessionConfigurationTest class is a test suite of test cases testing the contract and functionality
* of the {@link GemFireHttpSessionConfiguration} class.
* The GemFireHttpSessionConfigurationTest class is a test suite of test cases testing the
* contract and functionality of the {@link GemFireHttpSessionConfiguration} class.
*
* @author John Blum
* @since 1.1.0
@@ -51,7 +51,8 @@ import static org.mockito.Mockito.verify;
* @see org.springframework.data.gemfire.GemfireOperations
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.session.data.gemfire.GemFireOperationsSessionRepository
* @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration
* @see org.springframework.session.data.gemfire.config.annotation.web.http.
* GemFireHttpSessionConfiguration
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.Region
@@ -74,9 +75,11 @@ public class GemFireHttpSessionConfigurationTest {
public void setAndGetBeanClassLoader() {
assertThat(this.gemfireConfiguration.getBeanClassLoader()).isNull();
this.gemfireConfiguration.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
this.gemfireConfiguration
.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
assertThat(this.gemfireConfiguration.getBeanClassLoader()).isEqualTo(Thread.currentThread().getContextClassLoader());
assertThat(this.gemfireConfiguration.getBeanClassLoader())
.isEqualTo(Thread.currentThread().getContextClassLoader());
this.gemfireConfiguration.setBeanClassLoader(null);
@@ -86,138 +89,168 @@ public class GemFireHttpSessionConfigurationTest {
@Test
public void setAndGetClientRegionShortcut() {
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration
.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getClientRegionShortcut())
.isEqualTo(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration.setClientRegionShortcut(null);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
GemFireHttpSessionConfiguration.DEFAULT_CLIENT_REGION_SHORTCUT);
}
@Test
public void setAndGetIndexableSessionAttributes() {
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
this.gemfireConfiguration.setIndexableSessionAttributes(toArray("one", "two", "three"));
this.gemfireConfiguration
.setIndexableSessionAttributes(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression())
.isEqualTo("'one', 'two', 'three'");
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes())
.isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration
.getIndexableSessionAttributesAsGemFireIndexExpression())
.isEqualTo("'one', 'two', 'three'");
this.gemfireConfiguration.setIndexableSessionAttributes(toArray("one"));
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one"));
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("'one'");
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes())
.isEqualTo(toArray("one"));
assertThat(this.gemfireConfiguration
.getIndexableSessionAttributesAsGemFireIndexExpression())
.isEqualTo("'one'");
this.gemfireConfiguration.setIndexableSessionAttributes(null);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
GemFireHttpSessionConfiguration.DEFAULT_INDEXABLE_SESSION_ATTRIBUTES);
assertThat(this.gemfireConfiguration
.getIndexableSessionAttributesAsGemFireIndexExpression()).isEqualTo("*");
}
@Test
public void setAndGetMaxInactiveIntervalInSeconds() {
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS);
GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(300);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(300);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds())
.isEqualTo(300);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(Integer.MAX_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(Integer.MAX_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds())
.isEqualTo(Integer.MAX_VALUE);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(-1);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(-1);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds())
.isEqualTo(-1);
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(Integer.MIN_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(Integer.MIN_VALUE);
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds())
.isEqualTo(Integer.MIN_VALUE);
}
@Test
public void setAndGetServerRegionShortcut() {
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE_PERSISTENT);
this.gemfireConfiguration
.setServerRegionShortcut(RegionShortcut.REPLICATE_PERSISTENT);
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE_PERSISTENT);
assertThat(this.gemfireConfiguration.getServerRegionShortcut())
.isEqualTo(RegionShortcut.REPLICATE_PERSISTENT);
this.gemfireConfiguration.setServerRegionShortcut(null);
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
GemFireHttpSessionConfiguration.DEFAULT_SERVER_REGION_SHORTCUT);
}
@Test
public void setAndGetSpringSessionGemFireRegionName() {
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("test");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("test");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo("test");
this.gemfireConfiguration.setSpringSessionGemFireRegionName(" ");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName("");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.gemfireConfiguration.setSpringSessionGemFireRegionName(null);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo(
GemFireHttpSessionConfiguration.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
@Test
public void setImportMetadata() {
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class, "testSetImportMetadata");
AnnotationMetadata mockAnnotationMetadata = mock(AnnotationMetadata.class,
"testSetImportMetadata");
Map<String, Object> annotationAttributes = new HashMap<String, Object>(4);
annotationAttributes.put("clientRegionShortcut", ClientRegionShortcut.CACHING_PROXY);
annotationAttributes.put("indexableSessionAttributes", toArray("one", "two", "three"));
annotationAttributes.put("clientRegionShortcut",
ClientRegionShortcut.CACHING_PROXY);
annotationAttributes.put("indexableSessionAttributes",
toArray("one", "two", "three"));
annotationAttributes.put("maxInactiveIntervalInSeconds", 600);
annotationAttributes.put("serverRegionShortcut", RegionShortcut.REPLICATE);
annotationAttributes.put("regionName", "TEST");
given(mockAnnotationMetadata.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())))
.willReturn(annotationAttributes);
given(mockAnnotationMetadata
.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName())))
.willReturn(annotationAttributes);
this.gemfireConfiguration.setImportMetadata(mockAnnotationMetadata);
assertThat(this.gemfireConfiguration.getClientRegionShortcut()).isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes()).isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds()).isEqualTo(600);
assertThat(this.gemfireConfiguration.getServerRegionShortcut()).isEqualTo(RegionShortcut.REPLICATE);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("TEST");
assertThat(this.gemfireConfiguration.getClientRegionShortcut())
.isEqualTo(ClientRegionShortcut.CACHING_PROXY);
assertThat(this.gemfireConfiguration.getIndexableSessionAttributes())
.isEqualTo(toArray("one", "two", "three"));
assertThat(this.gemfireConfiguration.getMaxInactiveIntervalInSeconds())
.isEqualTo(600);
assertThat(this.gemfireConfiguration.getServerRegionShortcut())
.isEqualTo(RegionShortcut.REPLICATE);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo("TEST");
verify(mockAnnotationMetadata, times(1)).getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName()));
verify(mockAnnotationMetadata, times(1))
.getAnnotationAttributes(eq(EnableGemFireHttpSession.class.getName()));
}
@Test
public void createAndInitializeSpringSessionRepositoryBean() {
GemfireOperations mockGemfireOperations = mock(GemfireOperations.class,
"testCreateAndInitializeSpringSessionRepositoryBean");
"testCreateAndInitializeSpringSessionRepositoryBean");
this.gemfireConfiguration.setMaxInactiveIntervalInSeconds(120);
GemFireOperationsSessionRepository sessionRepository = this.gemfireConfiguration.sessionRepository(
mockGemfireOperations);
GemFireOperationsSessionRepository sessionRepository = this.gemfireConfiguration
.sessionRepository(mockGemfireOperations);
assertThat(sessionRepository).isNotNull();
assertThat(sessionRepository.getTemplate()).isSameAs(mockGemfireOperations);
@@ -234,9 +267,11 @@ public class GemFireHttpSessionConfigurationTest {
this.gemfireConfiguration.setSpringSessionGemFireRegionName("Example");
GemfireTemplate template = this.gemfireConfiguration.sessionRegionTemplate(mockGemFireCache);
GemfireTemplate template = this.gemfireConfiguration
.sessionRegionTemplate(mockGemFireCache);
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName()).isEqualTo("Example");
assertThat(this.gemfireConfiguration.getSpringSessionGemFireRegionName())
.isEqualTo("Example");
assertThat(template).isNotNull();
assertThat(template.getRegion()).isSameAs(mockRegion);
@@ -246,37 +281,45 @@ public class GemFireHttpSessionConfigurationTest {
@Test
public void expirationIsAllowed() {
Cache mockCache = mock(Cache.class, "testExpirationIsAllowed.MockCache");
ClientCache mockClientCache = mock(ClientCache.class, "testExpirationIsAllowed.MockClientCache");
ClientCache mockClientCache = mock(ClientCache.class,
"testExpirationIsAllowed.MockClientCache");
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isTrue();
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW);
this.gemfireConfiguration.setServerRegionShortcut(
RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockCache)).isTrue();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration
.setClientRegionShortcut(ClientRegionShortcut.CACHING_PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_PROXY);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isTrue();
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache))
.isTrue();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW);
this.gemfireConfiguration
.setClientRegionShortcut(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.REPLICATE_PROXY);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isTrue();
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache))
.isTrue();
}
@Test
public void expirationIsNotAllowed() {
Cache mockCache = mock(Cache.class, "testExpirationIsAllowed.MockCache");
ClientCache mockClientCache = mock(ClientCache.class, "testExpirationIsAllowed.MockClientCache");
ClientCache mockClientCache = mock(ClientCache.class,
"testExpirationIsAllowed.MockClientCache");
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.PROXY);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION);
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache)).isFalse();
assertThat(this.gemfireConfiguration.isExpirationAllowed(mockClientCache))
.isFalse();
this.gemfireConfiguration.setClientRegionShortcut(ClientRegionShortcut.LOCAL);
this.gemfireConfiguration.setServerRegionShortcut(RegionShortcut.PARTITION_PROXY);

View File

@@ -39,15 +39,17 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* The GemFireCacheTypeAwareRegionFactoryBeanTest class is a test suite of test cases testing the contract
* and functionality of the GemFireCacheTypeAwareRegionFactoryBean class.
* The GemFireCacheTypeAwareRegionFactoryBeanTest class is a test suite of test cases
* testing the contract and functionality of the GemFireCacheTypeAwareRegionFactoryBean
* class.
*
* @author John Blum
* @since 1.1.0
* @see org.junit.Rule
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.GemFireCacheTypeAwareRegionFactoryBean
* @see org.springframework.session.data.gemfire.config.annotation.web.http.support.
* GemFireCacheTypeAwareRegionFactoryBean
* @see com.gemstone.gemfire.cache.Cache
* @see com.gemstone.gemfire.cache.GemFireCache
* @see com.gemstone.gemfire.cache.InterestResultPolicy
@@ -81,14 +83,18 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
public void afterPropertiesSetCreatesClientRegionForClientCache() throws Exception {
this.regionFactoryBean = new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>() {
@Override
protected Region<Object, ExpiringSession> newClientRegion(GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientCache);
protected Region<Object, ExpiringSession> newClientRegion(
GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(
GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientRegion;
}
@Override
protected Region<Object, ExpiringSession> newServerRegion(final GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientCache);
protected Region<Object, ExpiringSession> newServerRegion(
final GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(
GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockServerRegion;
}
};
@@ -96,7 +102,8 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
this.regionFactoryBean.setGemfireCache(this.mockClientCache);
this.regionFactoryBean.afterPropertiesSet();
assertThat(this.regionFactoryBean.getGemfireCache()).isSameAs(this.mockClientCache);
assertThat(this.regionFactoryBean.getGemfireCache())
.isSameAs(this.mockClientCache);
assertThat(this.regionFactoryBean.getObject()).isEqualTo(this.mockClientRegion);
}
@@ -106,13 +113,15 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
this.regionFactoryBean = new GemFireCacheTypeAwareRegionFactoryBean<Object, ExpiringSession>() {
@Override
protected Region<Object, ExpiringSession> newClientRegion(GemFireCache gemfireCache) throws Exception {
protected Region<Object, ExpiringSession> newClientRegion(
GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(mockCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockClientRegion;
}
@Override
protected Region<Object, ExpiringSession> newServerRegion(final GemFireCache gemfireCache) throws Exception {
protected Region<Object, ExpiringSession> newServerRegion(
final GemFireCache gemfireCache) throws Exception {
assertThat(gemfireCache).isSameAs(mockCache);
return GemFireCacheTypeAwareRegionFactoryBeanTest.this.mockServerRegion;
}
@@ -158,16 +167,18 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Test
public void setAndGetClientRegionShortcut() {
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
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);
assertThat(this.regionFactoryBean.getClientRegionShortcut())
.isEqualTo(ClientRegionShortcut.LOCAL_PERSISTENT);
this.regionFactoryBean.setClientRegionShortcut(null);
assertThat(this.regionFactoryBean.getClientRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_CLIENT_REGION_SHORTCUT);
}
@Test
@@ -182,27 +193,31 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Test
public void setGemfireCacheToNullThrowsIllegalArgumentException() {
this.expectedException.expect(IllegalArgumentException.class);
this.expectedException.expectMessage("The GemFireCache reference must not be null");
this.expectedException
.expectMessage("The GemFireCache reference 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.expectedException.expectMessage(
"A reference to a GemFireCache was not properly configured");
this.regionFactoryBean.getGemfireCache();
}
@Test
@SuppressWarnings("unchecked")
public void setAndGetRegionAttributes() {
RegionAttributes<Object, ExpiringSession> mockRegionAttributes = mock(RegionAttributes.class);
RegionAttributes<Object, ExpiringSession> mockRegionAttributes = mock(
RegionAttributes.class);
assertThat(this.regionFactoryBean.getRegionAttributes()).isNull();
this.regionFactoryBean.setRegionAttributes(mockRegionAttributes);
assertThat(this.regionFactoryBean.getRegionAttributes()).isSameAs(mockRegionAttributes);
assertThat(this.regionFactoryBean.getRegionAttributes())
.isSameAs(mockRegionAttributes);
this.regionFactoryBean.setRegionAttributes(null);
@@ -212,7 +227,7 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
@Test
public void setAndGetRegionName() {
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName("Example");
@@ -221,32 +236,33 @@ public class GemFireCacheTypeAwareRegionFactoryBeanTest {
this.regionFactoryBean.setRegionName(" ");
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName("");
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
this.regionFactoryBean.setRegionName(null);
assertThat(this.regionFactoryBean.getRegionName()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SPRING_SESSION_GEMFIRE_REGION_NAME);
}
@Test
public void setAndGetServerRegionShortcut() {
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
this.regionFactoryBean.setServerRegionShortcut(RegionShortcut.LOCAL_PERSISTENT);
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(RegionShortcut.LOCAL_PERSISTENT);
assertThat(this.regionFactoryBean.getServerRegionShortcut())
.isEqualTo(RegionShortcut.LOCAL_PERSISTENT);
this.regionFactoryBean.setServerRegionShortcut(null);
assertThat(this.regionFactoryBean.getServerRegionShortcut()).isEqualTo(
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
GemFireCacheTypeAwareRegionFactoryBean.DEFAULT_SERVER_REGION_SHORTCUT);
}
}

View File

@@ -33,8 +33,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* The GemFireUtilsTest class is a test suite of test cases testing the contract and functionality of the GemFireUtils
* utility class.
* The GemFireUtilsTest class is a test suite of test cases testing the contract and
* functionality of the GemFireUtils utility class.
*
* @author John Blum
* @since 1.1.0
@@ -52,7 +52,8 @@ public class GemFireUtilsTest {
}
@Test
public void closeNonNullCloseableObjectThrowingIOExceptionReturnsFalse() throws IOException {
public void closeNonNullCloseableObjectThrowingIOExceptionReturnsFalse()
throws IOException {
Closeable mockCloseable = mock(Closeable.class);
willThrow(new IOException("test")).given(mockCloseable).close();
assertThat(GemFireUtils.close(mockCloseable)).isFalse();
@@ -100,14 +101,17 @@ public class GemFireUtilsTest {
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_HEAP_LRU)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_OVERFLOW)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_PERSISTENT)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isTrue();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW))
.isTrue();
}
@Test
public void clientRegionShortcutIsNotLocal() {
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU))
.isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.CACHING_PROXY_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isLocal(ClientRegionShortcut.PROXY)).isFalse();
}
@@ -119,19 +123,23 @@ public class GemFireUtilsTest {
@Test
public void clientRegionShortcutIsNotProxy() {
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_HEAP_LRU))
.isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.CACHING_PROXY_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(ClientRegionShortcut.LOCAL_PERSISTENT_OVERFLOW))
.isFalse();
}
@Test
public void regionShortcutIsProxy() {
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY)).isTrue();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY_REDUNDANT)).isTrue();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PROXY_REDUNDANT))
.isTrue();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PROXY)).isTrue();
}
@@ -141,22 +149,30 @@ public class GemFireUtilsTest {
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.LOCAL_PERSISTENT_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.REPLICATE_PERSISTENT_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_PERSISTENT_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW)).isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_HEAP_LRU))
.isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_OVERFLOW))
.isFalse();
assertThat(GemFireUtils.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT))
.isFalse();
assertThat(GemFireUtils
.isProxy(RegionShortcut.PARTITION_REDUNDANT_PERSISTENT_OVERFLOW))
.isFalse();
}
@Test

View File

@@ -1,6 +1,22 @@
/*
* 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;
import org.junit.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextImpl;
@@ -13,16 +29,17 @@ public class AuthenticationParserTests {
@Test
public void shouldExtractName() {
//given
// given
String principalName = "john_the_springer";
SecurityContextImpl context = new SecurityContextImpl();
context.setAuthentication(new UsernamePasswordAuthenticationToken(principalName, null));
context.setAuthentication(
new UsernamePasswordAuthenticationToken(principalName, null));
//when
// when
String extractedName = AuthenticationParser.extractName(context);
//then
// then
assertThat(extractedName).isEqualTo(principalName);
}
}
}

View File

@@ -1,7 +1,23 @@
/*
* 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;
import com.mongodb.DBObject;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextImpl;
@@ -19,53 +35,60 @@ public class JdkMongoSessionConverterTests {
@Test
public void verifyRoundTripSerialization() throws Exception {
//given
// given
MongoExpiringSession toSerialize = new MongoExpiringSession();
toSerialize.setAttribute("username", "john_the_springer");
//when
// when
DBObject dbObject = convertToDBObject(toSerialize);
ExpiringSession deserialized = convertToSession(dbObject);
//then
// then
assertThat(deserialized).isEqualToComparingFieldByField(toSerialize);
}
@Test
public void shouldExtractPrincipalNameFromAttributes() throws Exception {
//given
// given
MongoExpiringSession toSerialize = new MongoExpiringSession();
String principalName = "john_the_springer";
toSerialize.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principalName);
toSerialize.setAttribute(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principalName);
//when
// when
DBObject dbObject = convertToDBObject(toSerialize);
//then
// then
assertThat(dbObject.get("principal")).isEqualTo(principalName);
}
@Test
public void shouldExtractPrincipalNameFromAuthentication() throws Exception {
//given
// given
MongoExpiringSession toSerialize = new MongoExpiringSession();
String principalName = "john_the_springer";
SecurityContextImpl context = new SecurityContextImpl();
context.setAuthentication(new UsernamePasswordAuthenticationToken(principalName, null));
context.setAuthentication(
new UsernamePasswordAuthenticationToken(principalName, null));
toSerialize.setAttribute("SPRING_SECURITY_CONTEXT", context);
//when
// when
DBObject dbObject = convertToDBObject(toSerialize);
//then
// then
assertThat(dbObject.get("principal")).isEqualTo(principalName);
}
MongoExpiringSession convertToSession(DBObject session) {
return (MongoExpiringSession) sut.convert(session, TypeDescriptor.valueOf(DBObject.class), TypeDescriptor.valueOf(MongoExpiringSession.class));
return (MongoExpiringSession) this.sut.convert(session,
TypeDescriptor.valueOf(DBObject.class),
TypeDescriptor.valueOf(MongoExpiringSession.class));
}
DBObject convertToDBObject(MongoExpiringSession session) {
return (DBObject) sut.convert(session, TypeDescriptor.valueOf(MongoExpiringSession.class), TypeDescriptor.valueOf(DBObject.class));
return (DBObject) this.sut.convert(session,
TypeDescriptor.valueOf(MongoExpiringSession.class),
TypeDescriptor.valueOf(DBObject.class));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* 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.
@@ -15,6 +15,10 @@
*/
package org.springframework.session.data.mongo;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
@@ -24,23 +28,20 @@ import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jakub Kubrynski
@@ -57,114 +58,132 @@ public class MongoOperationsSessionRepositoryTests {
@Before
public void setUp() throws Exception {
sut = new MongoOperationsSessionRepository(mongoOperations);
sut.setMongoSessionConverter(converter);
this.sut = new MongoOperationsSessionRepository(this.mongoOperations);
this.sut.setMongoSessionConverter(this.converter);
}
@Test
public void shouldCreateSession() throws Exception {
//when
ExpiringSession session = sut.createSession();
// when
ExpiringSession session = this.sut.createSession();
//then
// then
assertThat(session.getId()).isNotEmpty();
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(MongoOperationsSessionRepository.DEFAULT_INACTIVE_INTERVAL);
}
@Test
public void shouldSaveSession() throws Exception {
//given
// given
MongoExpiringSession session = new MongoExpiringSession();
BasicDBObject dbSession = new BasicDBObject();
DBCollection collection = mock(DBCollection.class);
when(converter.convert(session, TypeDescriptor.valueOf(MongoExpiringSession.class), TypeDescriptor.valueOf(DBObject.class))).thenReturn(dbSession);
when(mongoOperations.getCollection(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).thenReturn(collection);
//when
sut.save(session);
given(this.converter.convert(session,
TypeDescriptor.valueOf(MongoExpiringSession.class),
TypeDescriptor.valueOf(DBObject.class))).willReturn(dbSession);
given(this.mongoOperations
.getCollection(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME))
.willReturn(collection);
// when
this.sut.save(session);
//then
// then
verify(collection).save(dbSession);
}
@Test
public void shouldGetSession() throws Exception {
//given
// given
String sessionId = UUID.randomUUID().toString();
BasicDBObject dbSession = new BasicDBObject();
when(mongoOperations.findById(sessionId, DBObject.class, MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).thenReturn(dbSession);
given(this.mongoOperations.findById(sessionId, DBObject.class,
MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME))
.willReturn(dbSession);
MongoExpiringSession session = new MongoExpiringSession();
when(converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class), TypeDescriptor.valueOf(MongoExpiringSession.class))).thenReturn(session);
given(this.converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class),
TypeDescriptor.valueOf(MongoExpiringSession.class))).willReturn(session);
//when
ExpiringSession retrievedSession = sut.getSession(sessionId);
// when
ExpiringSession retrievedSession = this.sut.getSession(sessionId);
//then
// then
assertThat(retrievedSession).isEqualTo(session);
}
@Test
public void shouldHandleExpiredSession() throws Exception {
//given
// given
String sessionId = UUID.randomUUID().toString();
BasicDBObject dbSession = new BasicDBObject();
when(mongoOperations.findById(sessionId, DBObject.class, MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)).thenReturn(dbSession);
given(this.mongoOperations.findById(sessionId, DBObject.class,
MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME))
.willReturn(dbSession);
MongoExpiringSession session = mock(MongoExpiringSession.class);
when(session.isExpired()).thenReturn(true);
when(session.getId()).thenReturn(sessionId);
when(converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class), TypeDescriptor.valueOf(MongoExpiringSession.class))).thenReturn(session);
given(session.isExpired()).willReturn(true);
given(session.getId()).willReturn(sessionId);
given(this.converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class),
TypeDescriptor.valueOf(MongoExpiringSession.class))).willReturn(session);
//when
sut.getSession(sessionId);
// when
this.sut.getSession(sessionId);
//then
verify(mongoOperations).remove(any(DBObject.class), eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
// then
verify(this.mongoOperations).remove(any(DBObject.class),
eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
}
@Test
public void shouldDeleteSession() throws Exception {
//given
// given
String sessionId = UUID.randomUUID().toString();
//when
sut.delete(sessionId);
// when
this.sut.delete(sessionId);
//then
verify(mongoOperations).remove(any(DBObject.class), eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
// then
verify(this.mongoOperations).remove(any(DBObject.class),
eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME));
}
@Test
public void shouldGetSessionsMapByPrincipal() throws Exception {
//given
// given
String principalNameIndexName = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
DBObject dbSession = new BasicDBObject();
when(converter.getQueryForIndex(anyString(), Matchers.anyObject())).thenReturn(mock(Query.class));
when(mongoOperations.find(any(Query.class), eq(DBObject.class), eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)))
.thenReturn(Collections.singletonList(dbSession));
given(this.converter.getQueryForIndex(anyString(), Matchers.anyObject()))
.willReturn(mock(Query.class));
given(this.mongoOperations.find(any(Query.class), eq(DBObject.class),
eq(MongoOperationsSessionRepository.DEFAULT_COLLECTION_NAME)))
.willReturn(Collections.singletonList(dbSession));
String sessionId = UUID.randomUUID().toString();
MongoExpiringSession session = new MongoExpiringSession(sessionId, 1800);
when(converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class), TypeDescriptor.valueOf(MongoExpiringSession.class))).thenReturn(session);
//when
Map<String, MongoExpiringSession> sessionsMap = sut.findByIndexNameAndIndexValue(principalNameIndexName, "john");
given(this.converter.convert(dbSession, TypeDescriptor.valueOf(DBObject.class),
TypeDescriptor.valueOf(MongoExpiringSession.class))).willReturn(session);
// when
Map<String, MongoExpiringSession> sessionsMap = this.sut
.findByIndexNameAndIndexValue(principalNameIndexName, "john");
//then
// then
assertThat(sessionsMap).containsOnlyKeys(sessionId);
assertThat(sessionsMap).containsValues(session);
}
@Test
public void shouldReturnEmptyMapForNotSupportedIndex() throws Exception {
//given
// given
String index = "some_not_supported_index_name";
//when
Map<String, MongoExpiringSession> sessionsMap = sut.findByIndexNameAndIndexValue(index, "some_value");
// when
Map<String, MongoExpiringSession> sessionsMap = this.sut
.findByIndexNameAndIndexValue(index, "some_value");
//then
// then
assertThat(sessionsMap).isEmpty();
}
}
}

View File

@@ -67,7 +67,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings({"unchecked", "rawtypes"})
@SuppressWarnings({ "unchecked", "rawtypes" })
public class RedisOperationsSessionRepositoryTests {
static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
@@ -96,7 +96,6 @@ public class RedisOperationsSessionRepositoryTests {
private RedisOperationsSessionRepository redisRepository;
@Before
public void setup() {
this.redisRepository = new RedisOperationsSessionRepository(this.redisOperations);
@@ -132,7 +131,8 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void createSessionDefaultMaxInactiveInterval() throws Exception {
ExpiringSession session = this.redisRepository.createSession();
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
}
@Test
@@ -146,18 +146,24 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void saveNewSession() {
RedisSession session = this.redisRepository.createSession();
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
Map<String, Object> delta = getDelta();
assertThat(delta.size()).isEqualTo(3);
Object creationTime = delta.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
Object creationTime = delta
.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
assertThat(creationTime).isEqualTo(session.getCreationTime());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)).isEqualTo(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR)).isEqualTo(session.getCreationTime());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR))
.isEqualTo(session.getCreationTime());
}
@Test
@@ -165,19 +171,28 @@ public class RedisOperationsSessionRepositoryTests {
RedisSession session = this.redisRepository.createSession();
String sessionKey = "spring:session:sessions:" + session.getId();
String backgroundExpireKey = "spring:session:expirations:" + RedisSessionExpirationPolicy.roundUpToNextMinute(RedisSessionExpirationPolicy.expiresInMillis(session));
String backgroundExpireKey = "spring:session:expirations:"
+ RedisSessionExpirationPolicy.roundUpToNextMinute(
RedisSessionExpirationPolicy.expiresInMillis(session));
String destroyedTriggerKey = "spring:session:sessions:expires:" + session.getId();
given(this.redisOperations.boundHashOps(sessionKey)).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(backgroundExpireKey)).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(destroyedTriggerKey)).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(sessionKey))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(backgroundExpireKey))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(destroyedTriggerKey))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
// the actual data in the session expires 5 minutes after expiration so the data can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since getSession checks if it is expired
long fiveMinutesAfterExpires = session.getMaxInactiveIntervalInSeconds() + TimeUnit.MINUTES.toSeconds(5);
verify(this.boundHashOperations).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
// the actual data in the session expires 5 minutes after expiration so the data
// can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since
// getSession checks if it is expired
long fiveMinutesAfterExpires = session.getMaxInactiveIntervalInSeconds()
+ TimeUnit.MINUTES.toSeconds(5);
verify(this.boundHashOperations).expire(fiveMinutesAfterExpires,
TimeUnit.SECONDS);
verify(this.boundSetOperations).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
verify(this.boundSetOperations).add("expires:" + session.getId());
verify(this.boundValueOperations).expire(1800L, TimeUnit.SECONDS);
@@ -188,28 +203,43 @@ public class RedisOperationsSessionRepositoryTests {
public void saveJavadoc() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id")).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps("spring:session:expirations:1404361860000")).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps("spring:session:sessions:expires:session-id")).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps("spring:session:sessions:session-id"))
.willReturn(this.boundHashOperations);
given(this.redisOperations
.boundSetOps("spring:session:expirations:1404361860000"))
.willReturn(this.boundSetOperations);
given(this.redisOperations
.boundValueOps("spring:session:sessions:expires:session-id"))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
// the actual data in the session expires 5 minutes after expiration so the data can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since getSession checks if it is expired
verify(this.boundHashOperations).expire(session.getMaxInactiveIntervalInSeconds() + TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
// the actual data in the session expires 5 minutes after expiration so the data
// can be accessed in expiration events
// if the session is retrieved and expired it will not be returned since
// getSession checks if it is expired
verify(this.boundHashOperations).expire(
session.getMaxInactiveIntervalInSeconds() + TimeUnit.MINUTES.toSeconds(5),
TimeUnit.SECONDS);
}
@Test
public void saveLastAccessChanged() {
RedisSession session = this.redisRepository.new RedisSession(new MapSession(this.cached));
RedisSession session = this.redisRepository.new RedisSession(
new MapSession(this.cached));
session.setLastAccessedTime(12345678L);
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta()).isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, session.getLastAccessedTime()));
assertThat(getDelta())
.isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
session.getLastAccessedTime()));
}
@Test
@@ -217,13 +247,18 @@ public class RedisOperationsSessionRepositoryTests {
String attrName = "attrName";
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta()).isEqualTo(map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), session.getAttribute(attrName)));
assertThat(getDelta()).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName)));
}
@Test
@@ -231,22 +266,29 @@ public class RedisOperationsSessionRepositoryTests {
String attrName = "attrName";
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.removeAttribute(attrName);
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
assertThat(getDelta()).isEqualTo(map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), null));
assertThat(getDelta()).isEqualTo(map(
RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), null));
}
@Test
public void saveExpired() {
RedisSession session = this.redisRepository.new RedisSession(new MapSession());
session.setMaxInactiveIntervalInSeconds(0);
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.save(session);
@@ -272,30 +314,41 @@ public class RedisOperationsSessionRepositoryTests {
MapSession expected = new MapSession();
expected.setLastAccessedTime(System.currentTimeMillis() - 60000);
expected.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
Map map = map(
RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), expected.getAttribute(attrName),
RedisOperationsSessionRepository.CREATION_TIME_ATTR, expected.getCreationTime(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, expected.getMaxInactiveIntervalInSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, expected.getLastAccessedTime());
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
expected.getAttribute(attrName),
RedisOperationsSessionRepository.CREATION_TIME_ATTR,
expected.getCreationTime(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR,
expected.getMaxInactiveIntervalInSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
expected.getLastAccessedTime());
given(this.boundHashOperations.entries()).willReturn(map);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
String id = expected.getId();
this.redisRepository.delete(id);
assertThat(getDelta().get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)).isEqualTo(0);
assertThat(getDelta().get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo(0);
verify(this.redisOperations, atLeastOnce()).delete(getKey("expires:" + id));
verify(this.redisOperations, never()).boundValueOps(getKey("expires:" + id));
}
@Test
public void deleteNullSession() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
String id = "abc";
this.redisRepository.delete(id);
@@ -306,7 +359,8 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void getSessionNotFound() {
String id = "abc";
given(this.redisOperations.boundHashOps(getKey(id))).willReturn(this.boundHashOperations);
given(this.redisOperations.boundHashOps(getKey(id)))
.willReturn(this.boundHashOperations);
given(this.boundHashOperations.entries()).willReturn(map());
assertThat(this.redisRepository.getSession(id)).isNull();
@@ -318,31 +372,39 @@ public class RedisOperationsSessionRepositoryTests {
MapSession expected = new MapSession();
expected.setLastAccessedTime(System.currentTimeMillis() - 60000);
expected.setAttribute(attrName, "attrValue");
given(this.redisOperations.boundHashOps(getKey(expected.getId()))).willReturn(this.boundHashOperations);
Map map = map(
RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), expected.getAttribute(attrName),
RedisOperationsSessionRepository.CREATION_TIME_ATTR, expected.getCreationTime(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, expected.getMaxInactiveIntervalInSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, expected.getLastAccessedTime());
given(this.redisOperations.boundHashOps(getKey(expected.getId())))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
expected.getAttribute(attrName),
RedisOperationsSessionRepository.CREATION_TIME_ATTR,
expected.getCreationTime(),
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR,
expected.getMaxInactiveIntervalInSeconds(),
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
expected.getLastAccessedTime());
given(this.boundHashOperations.entries()).willReturn(map);
RedisSession session = this.redisRepository.getSession(expected.getId());
assertThat(session.getId()).isEqualTo(expected.getId());
assertThat(session.getAttributeNames()).isEqualTo(expected.getAttributeNames());
assertThat(session.getAttribute(attrName)).isEqualTo(expected.getAttribute(attrName));
assertThat(session.getAttribute(attrName))
.isEqualTo(expected.getAttribute(attrName));
assertThat(session.getCreationTime()).isEqualTo(expected.getCreationTime());
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(expected.getMaxInactiveIntervalInSeconds());
assertThat(session.getLastAccessedTime()).isEqualTo(expected.getLastAccessedTime());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(expected.getMaxInactiveIntervalInSeconds());
assertThat(session.getLastAccessedTime())
.isEqualTo(expected.getLastAccessedTime());
}
@Test
public void getSessionExpired() {
String expiredId = "expired-id";
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
Map map = map(
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
given(this.boundHashOperations.entries()).willReturn(map);
assertThat(this.redisRepository.getSession(expiredId)).isNull();
@@ -351,15 +413,20 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void findByPrincipalNameExpired() {
String expiredId = "expired-id";
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.boundSetOperations.members()).willReturn(Collections.<Object>singleton(expiredId));
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
Map map = map(
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.boundSetOperations.members())
.willReturn(Collections.<Object>singleton(expiredId));
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, 1,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(5));
given(this.boundHashOperations.entries()).willReturn(map);
assertThat(this.redisRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "principal")).isEmpty();
assertThat(this.redisRepository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "principal"))
.isEmpty();
}
@Test
@@ -368,16 +435,21 @@ public class RedisOperationsSessionRepositoryTests {
long createdTime = lastAccessed - 10;
int maxInactive = 3600;
String sessionId = "some-id";
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.boundSetOperations.members()).willReturn(Collections.<Object>singleton(sessionId));
given(this.redisOperations.boundHashOps(getKey(sessionId))).willReturn(this.boundHashOperations);
Map map = map(
RedisOperationsSessionRepository.CREATION_TIME_ATTR, createdTime,
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.boundSetOperations.members())
.willReturn(Collections.<Object>singleton(sessionId));
given(this.redisOperations.boundHashOps(getKey(sessionId)))
.willReturn(this.boundHashOperations);
Map map = map(RedisOperationsSessionRepository.CREATION_TIME_ATTR, createdTime,
RedisOperationsSessionRepository.MAX_INACTIVE_ATTR, maxInactive,
RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, lastAccessed);
given(this.boundHashOperations.entries()).willReturn(map);
Map<String, RedisSession> sessionIdToSessions = this.redisRepository.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "principal");
Map<String, RedisSession> sessionIdToSessions = this.redisRepository
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
"principal");
assertThat(sessionIdToSessions).hasSize(1);
RedisSession session = sessionIdToSessions.get(sessionId);
@@ -391,10 +463,13 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void cleanupExpiredSessions() {
String expiredId = "expired-id";
given(this.redisOperations.boundHashOps(getKey(expiredId))).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundHashOps(getKey(expiredId)))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
Set<Object> expiredIds = new HashSet<Object>(Arrays.asList("expired-key1", "expired-key2"));
Set<Object> expiredIds = new HashSet<Object>(
Arrays.asList("expired-key1", "expired-key2"));
given(this.boundSetOperations.members()).willReturn(expiredIds);
this.redisRepository.cleanupExpiredSessions();
@@ -431,7 +506,8 @@ public class RedisOperationsSessionRepositoryTests {
byte[] pattern = "".getBytes("UTF-8");
byte[] body = new byte[0];
String channel = "spring:session:event:created:" + session.getId();
given(this.defaultSerializer.deserialize(body)).willReturn(new HashMap<String, Object>());
given(this.defaultSerializer.deserialize(body))
.willReturn(new HashMap<String, Object>());
DefaultMessage message = new DefaultMessage(channel.getBytes("UTF-8"), body);
this.redisRepository.setApplicationEventPublisher(this.publisher);
@@ -447,7 +523,8 @@ public class RedisOperationsSessionRepositoryTests {
PrincipalNameResolver resolver = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER;
String username = "username";
RedisSession session = this.redisRepository.createSession();
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);
session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
username);
assertThat(resolver.resolvePrincipal(session)).isEqualTo(username);
}
@@ -455,7 +532,8 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void resolveIndexOnSecurityContext() {
String principal = "resolveIndexOnSecurityContext";
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication authentication = new UsernamePasswordAuthenticationToken(principal,
"notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext context = new SecurityContextImpl();
context.setAuthentication(authentication);
@@ -508,26 +586,35 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void flushModeImmediateCreate() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
Map<String, Object> delta = getDelta();
assertThat(delta.size()).isEqualTo(3);
Object creationTime = delta.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
Object creationTime = delta
.get(RedisOperationsSessionRepository.CREATION_TIME_ATTR);
assertThat(creationTime).isEqualTo(session.getCreationTime());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR)).isEqualTo(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR)).isEqualTo(session.getCreationTime());
assertThat(delta.get(RedisOperationsSessionRepository.MAX_INACTIVE_ATTR))
.isEqualTo(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
assertThat(delta.get(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR))
.isEqualTo(session.getCreationTime());
}
@Test
public void flushModeImmediateSetAttribute() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
@@ -536,14 +623,19 @@ public class RedisOperationsSessionRepositoryTests {
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta).isEqualTo(map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), session.getAttribute(attrName)));
assertThat(delta).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName)));
}
@Test
public void flushModeImmediateRemoveAttribute() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
@@ -552,14 +644,19 @@ public class RedisOperationsSessionRepositoryTests {
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta).isEqualTo(map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName), session.getAttribute(attrName)));
assertThat(delta).isEqualTo(
map(RedisOperationsSessionRepository.getSessionAttrNameKey(attrName),
session.getAttribute(attrName)));
}
@Test
public void flushModeSetMaxInactiveIntervalInSeconds() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
@@ -573,9 +670,12 @@ public class RedisOperationsSessionRepositoryTests {
@Test
public void flushModeSetLastAccessedTime() {
given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations);
given(this.redisOperations.boundHashOps(anyString()))
.willReturn(this.boundHashOperations);
given(this.redisOperations.boundSetOps(anyString()))
.willReturn(this.boundSetOperations);
given(this.redisOperations.boundValueOps(anyString()))
.willReturn(this.boundValueOperations);
this.redisRepository.setRedisFlushMode(RedisFlushMode.IMMEDIATE);
RedisSession session = this.redisRepository.createSession();
@@ -585,7 +685,9 @@ public class RedisOperationsSessionRepositoryTests {
Map<String, Object> delta = getDelta(2);
assertThat(delta.size()).isEqualTo(1);
assertThat(delta).isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR, session.getLastAccessedTime()));
assertThat(delta)
.isEqualTo(map(RedisOperationsSessionRepository.LAST_ACCESSED_ATTR,
session.getLastAccessedTime()));
}
@Test(expected = IllegalArgumentException.class)
@@ -597,7 +699,7 @@ public class RedisOperationsSessionRepositoryTests {
return "spring:session:sessions:" + id;
}
private Map map(Object...objects) {
private Map map(Object... objects) {
Map<String, Object> result = new HashMap<String, Object>();
if (objects == null) {
return result;

View File

@@ -61,23 +61,31 @@ public class RedisSessionExpirationPolicyTests {
@Before
public void setup() {
RedisOperationsSessionRepository repository = new RedisOperationsSessionRepository(this.sessionRedisOperations);
this.policy = new RedisSessionExpirationPolicy(this.sessionRedisOperations, repository);
RedisOperationsSessionRepository repository = new RedisOperationsSessionRepository(
this.sessionRedisOperations);
this.policy = new RedisSessionExpirationPolicy(this.sessionRedisOperations,
repository);
this.session = new MapSession();
this.session.setLastAccessedTime(1429116694675L);
this.session.setId("12345");
given(this.sessionRedisOperations.boundSetOps(anyString())).willReturn(this.setOperations);
given(this.sessionRedisOperations.boundHashOps(anyString())).willReturn(this.hashOperations);
given(this.sessionRedisOperations.boundValueOps(anyString())).willReturn(this.valueOperations);
given(this.sessionRedisOperations.boundSetOps(anyString()))
.willReturn(this.setOperations);
given(this.sessionRedisOperations.boundHashOps(anyString()))
.willReturn(this.hashOperations);
given(this.sessionRedisOperations.boundValueOps(anyString()))
.willReturn(this.valueOperations);
}
// gh-169
@Test
public void onExpirationUpdatedRemovesOriginalExpirationTimeRoundedUp() throws Exception {
public void onExpirationUpdatedRemovesOriginalExpirationTimeRoundedUp()
throws Exception {
long originalExpirationTimeInMs = ONE_MINUTE_AGO;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy.getExpirationKey(originalRoundedToNextMinInMs);
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy
.getExpirationKey(originalRoundedToNextMinInMs);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
@@ -87,10 +95,14 @@ public class RedisSessionExpirationPolicyTests {
}
@Test
public void onExpirationUpdatedDoNotSendDeleteWhenExpirationTimeDoesNotChange() throws Exception {
long originalExpirationTimeInMs = RedisSessionExpirationPolicy.expiresInMillis(this.session) - 10;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy.getExpirationKey(originalRoundedToNextMinInMs);
public void onExpirationUpdatedDoNotSendDeleteWhenExpirationTimeDoesNotChange()
throws Exception {
long originalExpirationTimeInMs = RedisSessionExpirationPolicy
.expiresInMillis(this.session) - 10;
long originalRoundedToNextMinInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(originalExpirationTimeInMs);
String originalExpireKey = this.policy
.getExpirationKey(originalRoundedToNextMinInMs);
this.policy.onExpirationUpdated(originalExpirationTimeInMs, this.session);
@@ -101,15 +113,18 @@ public class RedisSessionExpirationPolicyTests {
@Test
public void onExpirationUpdatedAddsExpirationTimeRoundedUp() throws Exception {
long expirationTimeInMs = RedisSessionExpirationPolicy.expiresInMillis(this.session);
long expirationRoundedUpInMs = RedisSessionExpirationPolicy.roundUpToNextMinute(expirationTimeInMs);
long expirationTimeInMs = RedisSessionExpirationPolicy
.expiresInMillis(this.session);
long expirationRoundedUpInMs = RedisSessionExpirationPolicy
.roundUpToNextMinute(expirationTimeInMs);
String expectedExpireKey = this.policy.getExpirationKey(expirationRoundedUpInMs);
this.policy.onExpirationUpdated(null, this.session);
verify(this.sessionRedisOperations).boundSetOps(expectedExpireKey);
verify(this.setOperations).add("expires:" + this.session.getId());
verify(this.setOperations).expire(this.session.getMaxInactiveIntervalInSeconds() + TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
verify(this.setOperations).expire(this.session.getMaxInactiveIntervalInSeconds()
+ TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
}
@Test
@@ -119,6 +134,7 @@ public class RedisSessionExpirationPolicyTests {
this.policy.onExpirationUpdated(null, this.session);
verify(this.sessionRedisOperations).boundHashOps(sessionKey);
verify(this.hashOperations).expire(this.session.getMaxInactiveIntervalInSeconds() + TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
verify(this.hashOperations).expire(this.session.getMaxInactiveIntervalInSeconds()
+ TimeUnit.MINUTES.toSeconds(5), TimeUnit.SECONDS);
}
}

View File

@@ -151,14 +151,16 @@ public class SessionMessageListenerTests {
@Test
public void onMessageEventPublisherErrorCaught() throws Exception {
mockMessage("__keyevent@0__:del", "spring:session:sessions:123");
willThrow(new IllegalStateException("Test Exceptions are caught")).given(this.eventPublisher).publishEvent(any(ApplicationEvent.class));
willThrow(new IllegalStateException("Test Exceptions are caught"))
.given(this.eventPublisher).publishEvent(any(ApplicationEvent.class));
this.listener.onMessage(this.message, this.pattern);
verify(this.eventPublisher).publishEvent(any(ApplicationEvent.class));
}
private void mockMessage(String channel, String body) throws UnsupportedEncodingException {
private void mockMessage(String channel, String body)
throws UnsupportedEncodingException {
given(this.message.getBody()).willReturn(bytes(body));
given(this.message.getChannel()).willReturn(bytes(channel));
}

View File

@@ -54,7 +54,8 @@ public class EnableRedisKeyspaceNotificationsInitializerTests {
public void setup() {
given(this.connectionFactory.getConnection()).willReturn(this.connection);
this.initializer = new RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
this.initializer = new RedisHttpSessionConfiguration.EnableRedisKeyspaceNotificationsInitializer(
this.connectionFactory, new ConfigureNotifyKeyspaceEventsAction());
}
@Test
@@ -157,7 +158,8 @@ public class EnableRedisKeyspaceNotificationsInitializerTests {
}
private void assertOptionsContains(String... expectedValues) {
verify(this.connection).setConfig(eq(CONFIG_NOTIFY_KEYSPACE_EVENTS), this.options.capture());
verify(this.connection).setConfig(eq(CONFIG_NOTIFY_KEYSPACE_EVENTS),
this.options.capture());
for (String expectedValue : expectedValues) {
assertThat(this.options.getValue()).contains(expectedValue);
}
@@ -165,6 +167,7 @@ public class EnableRedisKeyspaceNotificationsInitializerTests {
}
private void setConfigNotification(String value) {
given(this.connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS)).willReturn(Arrays.asList(CONFIG_NOTIFY_KEYSPACE_EVENTS, value));
given(this.connection.getConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS))
.willReturn(Arrays.asList(CONFIG_NOTIFY_KEYSPACE_EVENTS, value));
}
}

View File

@@ -51,7 +51,8 @@ public class RedisHttpSessionConfigurationOverrideDefaultSerializerTests {
@Test
public void overrideDefaultRedisTemplate() {
assertThat(this.template.getDefaultSerializer()).isSameAs(this.defaultRedisSerializer);
assertThat(this.template.getDefaultSerializer())
.isSameAs(this.defaultRedisSerializer);
}
@EnableRedisHttpSession

View File

@@ -55,7 +55,8 @@ public class RedisHttpSessionConfigurationOverrideSessionTaskExecutor {
@Test
public void overrideSessionTaskExecutor() {
verify(this.springSessionRedisTaskExecutor, times(1)).execute(any(SchedulingAwareRunnable.class));
verify(this.springSessionRedisTaskExecutor, times(1))
.execute(any(SchedulingAwareRunnable.class));
}
@EnableRedisHttpSession

View File

@@ -59,7 +59,8 @@ public class RedisHttpSessionConfigurationOverrideSessionTaskExecutors {
@Test
public void overrideSessionTaskExecutors() {
verify(this.springSessionRedisSubscriptionExecutor, times(1)).execute(any(SchedulingAwareRunnable.class));
verify(this.springSessionRedisSubscriptionExecutor, times(1))
.execute(any(SchedulingAwareRunnable.class));
verify(this.springSessionRedisTaskExecutor, never()).execute(any(Runnable.class));
}

View File

@@ -56,12 +56,13 @@ public class Gh109Tests {
int sessionTimeout = 100;
/**
* override sessionRepository construction to set the custom
* session-timeout
* override sessionRepository construction to set the custom session-timeout
*/
@Bean
@Override
public RedisOperationsSessionRepository sessionRepository(RedisOperations<Object, Object> sessionRedisTemplate, ApplicationEventPublisher applicationEventPublisher) {
public RedisOperationsSessionRepository sessionRepository(
RedisOperations<Object, Object> sessionRedisTemplate,
ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
sessionRedisTemplate);
sessionRepository.setDefaultMaxInactiveInterval(this.sessionTimeout);

View File

@@ -42,6 +42,7 @@ import org.springframework.test.util.ReflectionTestUtils;
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;
import static org.mockito.Matchers.eq;
@@ -50,7 +51,6 @@ import static org.mockito.Matchers.startsWith;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/**
* Tests for {@link JdbcOperationsSessionRepository}.
@@ -81,10 +81,11 @@ public class JdbcOperationsSessionRepositoryTests {
@Test
public void constructorDataSource() {
JdbcOperationsSessionRepository repository =
new JdbcOperationsSessionRepository(this.dataSource);
JdbcOperationsSessionRepository repository = new JdbcOperationsSessionRepository(
this.dataSource);
assertThat(ReflectionTestUtils.getField(repository, "jdbcOperations")).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "jdbcOperations"))
.isNotNull();
}
@Test
@@ -137,11 +138,12 @@ public class JdbcOperationsSessionRepositoryTests {
@Test
public void createSessionDefaultMaxInactiveInterval() throws Exception {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
assertThat(session.isNew()).isTrue();
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(
new MapSession().getMaxInactiveIntervalInSeconds());
assertThat(session.getMaxInactiveIntervalInSeconds())
.isEqualTo(new MapSession().getMaxInactiveIntervalInSeconds());
verifyZeroInteractions(this.jdbcOperations);
}
@@ -150,7 +152,8 @@ public class JdbcOperationsSessionRepositoryTests {
int interval = 1;
this.repository.setDefaultMaxInactiveInterval(interval);
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
assertThat(session.isNew()).isTrue();
assertThat(session.getMaxInactiveIntervalInSeconds()).isEqualTo(interval);
@@ -159,41 +162,48 @@ public class JdbcOperationsSessionRepositoryTests {
@Test
public void saveNew() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
this.repository.save(session);
assertThat(session.isNew()).isFalse();
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT"), isA(PreparedStatementSetter.class));
verify(this.jdbcOperations, times(1)).update(startsWith("INSERT"),
isA(PreparedStatementSetter.class));
}
@Test
public void saveUpdatedAttributes() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(new MapSession());
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(
new MapSession());
session.setAttribute("testName", "testValue");
this.repository.save(session);
assertThat(session.isNew()).isFalse();
verify(this.jdbcOperations, times(1))
.update(and(startsWith("UPDATE"), contains("SESSION_BYTES")), isA(PreparedStatementSetter.class));
verify(this.jdbcOperations, times(1)).update(
and(startsWith("UPDATE"), contains("SESSION_BYTES")),
isA(PreparedStatementSetter.class));
}
@Test
public void saveUpdatedLastAccessedTime() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(new MapSession());
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(
new MapSession());
session.setLastAccessedTime(System.currentTimeMillis());
this.repository.save(session);
assertThat(session.isNew()).isFalse();
verify(this.jdbcOperations, times(1))
.update(and(startsWith("UPDATE"), not(contains("SESSION_BYTES"))), isA(PreparedStatementSetter.class));
verify(this.jdbcOperations, times(1)).update(
and(startsWith("UPDATE"), not(contains("SESSION_BYTES"))),
isA(PreparedStatementSetter.class));
}
@Test
public void saveUnchanged() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(new MapSession());
JdbcOperationsSessionRepository.JdbcSession session = this.repository.new JdbcSession(
new MapSession());
this.repository.save(session);
@@ -206,11 +216,12 @@ public class JdbcOperationsSessionRepositoryTests {
public void getSessionNotFound() {
String sessionId = "testSessionId";
JdbcOperationsSessionRepository.JdbcSession session = this.repository.getSession(sessionId);
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.getSession(sessionId);
assertThat(session).isNull();
verify(this.jdbcOperations, times(1)).queryForObject(
startsWith("SELECT"), eq(new Object[] { sessionId }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { sessionId }), isA(RowMapper.class));
}
@Test
@@ -218,15 +229,18 @@ public class JdbcOperationsSessionRepositoryTests {
public void getSessionExpired() {
MapSession expired = new MapSession();
expired.setMaxInactiveIntervalInSeconds(0);
when(this.jdbcOperations.queryForObject(startsWith("SELECT"), eq(new Object[] { expired.getId() }), isA(RowMapper.class)))
.thenReturn(expired);
given(this.jdbcOperations.queryForObject(startsWith("SELECT"),
eq(new Object[] { expired.getId() }), isA(RowMapper.class)))
.willReturn(expired);
JdbcOperationsSessionRepository.JdbcSession session = this.repository.getSession(expired.getId());
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.getSession(expired.getId());
assertThat(session).isNull();
verify(this.jdbcOperations, times(1)).queryForObject(
startsWith("SELECT"), eq(new Object[] { expired.getId() }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(expired.getId()));
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { expired.getId() }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"),
eq(expired.getId()));
}
@Test
@@ -234,16 +248,18 @@ public class JdbcOperationsSessionRepositoryTests {
public void getSessionFound() {
MapSession saved = new MapSession();
saved.setAttribute("savedName", "savedValue");
when(this.jdbcOperations.queryForObject(startsWith("SELECT"), eq(new Object[] { saved.getId() }), isA(RowMapper.class)))
.thenReturn(saved);
given(this.jdbcOperations.queryForObject(startsWith("SELECT"),
eq(new Object[] { saved.getId() }), isA(RowMapper.class)))
.willReturn(saved);
JdbcOperationsSessionRepository.JdbcSession session = this.repository.getSession(saved.getId());
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.getSession(saved.getId());
assertThat(session.getId()).isEqualTo(saved.getId());
assertThat(session.isNew()).isFalse();
assertThat(session.getAttribute("savedName")).isEqualTo("savedValue");
verify(this.jdbcOperations, times(1)).queryForObject(
startsWith("SELECT"), eq(new Object[] { saved.getId() }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).queryForObject(startsWith("SELECT"),
eq(new Object[] { saved.getId() }), isA(RowMapper.class));
}
@Test
@@ -259,8 +275,8 @@ public class JdbcOperationsSessionRepositoryTests {
public void findByIndexNameAndIndexValueUnknownIndexName() {
String indexValue = "testIndexValue";
Map<String, JdbcOperationsSessionRepository.JdbcSession> sessions =
this.repository.findByIndexNameAndIndexValue("testIndexName", indexValue);
Map<String, JdbcOperationsSessionRepository.JdbcSession> sessions = this.repository
.findByIndexNameAndIndexValue("testIndexName", indexValue);
assertThat(sessions).isEmpty();
verifyZeroInteractions(this.jdbcOperations);
@@ -272,19 +288,21 @@ public class JdbcOperationsSessionRepositoryTests {
String principal = "username";
Map<String, JdbcOperationsSessionRepository.JdbcSession> sessions = this.repository
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal);
assertThat(sessions).isEmpty();
verify(this.jdbcOperations, times(1)).query(
startsWith("SELECT"), eq(new Object[] { principal }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class));
}
@Test
@SuppressWarnings("unchecked")
public void findByIndexNameAndIndexValuePrincipalIndexNameFound() {
String principal = "username";
Authentication authentication = new UsernamePasswordAuthenticationToken(
principal, "notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
Authentication authentication = new UsernamePasswordAuthenticationToken(principal,
"notused", AuthorityUtils.createAuthorityList("ROLE_USER"));
List<MapSession> saved = new ArrayList<MapSession>(2);
MapSession saved1 = new MapSession();
saved1.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
@@ -292,15 +310,17 @@ public class JdbcOperationsSessionRepositoryTests {
MapSession saved2 = new MapSession();
saved2.setAttribute(SPRING_SECURITY_CONTEXT, authentication);
saved.add(saved2);
when(this.jdbcOperations.query(startsWith("SELECT"), eq(new Object[] { principal }), isA(RowMapper.class)))
.thenReturn(saved);
given(this.jdbcOperations.query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class))).willReturn(saved);
Map<String, JdbcOperationsSessionRepository.JdbcSession> sessions = this.repository
.findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, principal);
.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME,
principal);
assertThat(sessions).hasSize(2);
verify(this.jdbcOperations, times(1)).query(
startsWith("SELECT"), eq(new Object[] { principal }), isA(RowMapper.class));
verify(this.jdbcOperations, times(1)).query(startsWith("SELECT"),
eq(new Object[] { principal }), isA(RowMapper.class));
}
@Test

View File

@@ -74,16 +74,19 @@ public class JdbcHttpSessionConfigurationTests {
public void defaultConfiguration() {
registerAndRefresh(DefaultConfiguration.class);
assertThat(this.context.getBean(JdbcOperationsSessionRepository.class)).isNotNull();
assertThat(this.context.getBean(JdbcOperationsSessionRepository.class))
.isNotNull();
}
@Test
public void customTableName() {
registerAndRefresh(CustomTableNameConfiguration.class);
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
JdbcOperationsSessionRepository repository = this.context
.getBean(JdbcOperationsSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "tableName")).isEqualTo(TABLE_NAME);
assertThat(ReflectionTestUtils.getField(repository, "tableName"))
.isEqualTo(TABLE_NAME);
}
@Test
@@ -93,9 +96,11 @@ public class JdbcHttpSessionConfigurationTests {
try {
registerAndRefresh(DefaultConfiguration.class);
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
JdbcOperationsSessionRepository repository = this.context
.getBean(JdbcOperationsSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "tableName")).isEqualTo(TABLE_NAME);
assertThat(ReflectionTestUtils.getField(repository, "tableName"))
.isEqualTo(TABLE_NAME);
}
finally {
System.clearProperty(TABLE_NAME_SYSTEM_PROPERTY);
@@ -106,7 +111,8 @@ public class JdbcHttpSessionConfigurationTests {
public void customMaxInactiveIntervalInSeconds() {
registerAndRefresh(CustomMaxInactiveIntervalInSecondsConfiguration.class);
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
JdbcOperationsSessionRepository repository = this.context
.getBean(JdbcOperationsSessionRepository.class);
assertThat(repository).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "defaultMaxInactiveInterval"))
.isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS);
@@ -116,22 +122,27 @@ public class JdbcHttpSessionConfigurationTests {
public void customLobHandlerConfiguration() {
registerAndRefresh(CustomLobHandlerConfiguration.class);
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
JdbcOperationsSessionRepository repository = this.context
.getBean(JdbcOperationsSessionRepository.class);
LobHandler lobHandler = this.context.getBean(LobHandler.class);
assertThat(repository).isNotNull();
assertThat(lobHandler).isNotNull();
assertThat(ReflectionTestUtils.getField(repository, "lobHandler")).isEqualTo(lobHandler);
assertThat(ReflectionTestUtils.getField(repository, "lobHandler"))
.isEqualTo(lobHandler);
}
@Test
public void customConversionServiceConfiguration() {
registerAndRefresh(CustomDeserializingConverterConfiguration.class);
JdbcOperationsSessionRepository repository = this.context.getBean(JdbcOperationsSessionRepository.class);
ConversionService conversionService = this.context.getBean("springSessionConversionService", ConversionService.class);
JdbcOperationsSessionRepository repository = this.context
.getBean(JdbcOperationsSessionRepository.class);
ConversionService conversionService = this.context
.getBean("springSessionConversionService", ConversionService.class);
assertThat(repository).isNotNull();
assertThat(conversionService).isNotNull();
Object repositoryConversionService = ReflectionTestUtils.getField(repository, "conversionService");
Object repositoryConversionService = ReflectionTestUtils.getField(repository,
"conversionService");
assertThat(repositoryConversionService).isEqualTo(conversionService);
}
@@ -166,7 +177,8 @@ public class JdbcHttpSessionConfigurationTests {
@Configuration
@EnableJdbcHttpSession(maxInactiveIntervalInSeconds = MAX_INACTIVE_INTERVAL_IN_SECONDS)
static class CustomMaxInactiveIntervalInSecondsConfiguration extends BaseConfiguration {
static class CustomMaxInactiveIntervalInSecondsConfiguration
extends BaseConfiguration {
}
@Configuration

View File

@@ -55,14 +55,16 @@ public class CookieHttpSessionStrategyTests {
@Test
public void getRequestedSessionIdNotNull() throws Exception {
setSessionCookie(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request)).isEqualTo(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request))
.isEqualTo(this.session.getId());
}
@Test
public void getRequestedSessionIdNotNullCustomCookieName() throws Exception {
setCookieName("CUSTOM");
setSessionCookie(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request)).isEqualTo(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request))
.isEqualTo(this.session.getId());
}
@Test
@@ -105,15 +107,18 @@ public class CookieHttpSessionStrategyTests {
public void onNewSessionExistingSessionNewAlias() throws Exception {
Session existing = new MapSession();
setSessionCookie(existing.getId());
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.strategy.onNewSession(this.session, this.request, this.response);
assertThat(getSessionId()).isEqualTo("0 " + existing.getId() + " new " + this.session.getId());
assertThat(getSessionId())
.isEqualTo("0 " + existing.getId() + " new " + this.session.getId());
}
// gh-321
@Test
public void onNewSessionExplicitAlias() throws Exception {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.strategy.onNewSession(this.session, this.request, this.response);
assertThat(getSessionId()).isEqualTo("new " + this.session.getId());
}
@@ -124,7 +129,8 @@ public class CookieHttpSessionStrategyTests {
this.strategy.onNewSession(this.session, this.request, this.response);
Cookie sessionCookie = this.response.getCookie(this.cookieName);
assertThat(sessionCookie.getPath()).isEqualTo(this.request.getContextPath() + "/");
assertThat(sessionCookie.getPath())
.isEqualTo(this.request.getContextPath() + "/");
}
@Test
@@ -146,7 +152,8 @@ public class CookieHttpSessionStrategyTests {
this.strategy.onInvalidateSession(this.request, this.response);
Cookie sessionCookie = this.response.getCookie(this.cookieName);
assertThat(sessionCookie.getPath()).isEqualTo(this.request.getContextPath() + "/");
assertThat(sessionCookie.getPath())
.isEqualTo(this.request.getContextPath() + "/");
}
@Test
@@ -160,7 +167,8 @@ public class CookieHttpSessionStrategyTests {
public void onDeleteSessionExistingSessionSameAlias() throws Exception {
Session existing = new MapSession();
setSessionCookie("0 " + existing.getId() + " new " + this.session.getId());
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.strategy.onInvalidateSession(this.request, this.response);
assertThat(getSessionId()).isEqualTo(existing.getId());
}
@@ -169,7 +177,8 @@ public class CookieHttpSessionStrategyTests {
public void onDeleteSessionExistingSessionNewAlias() throws Exception {
Session existing = new MapSession();
setSessionCookie("0 " + existing.getId() + " new " + this.session.getId());
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "new");
this.strategy.onInvalidateSession(this.request, this.response);
assertThat(getSessionId()).isEqualTo(existing.getId());
}
@@ -197,34 +206,40 @@ public class CookieHttpSessionStrategyTests {
@Test
public void encodeURLExistingQueryExistingAliasStart() {
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "2")).isEqualTo("/url?_s=2&y=z");
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "2"))
.isEqualTo("/url?_s=2&y=z");
}
@Test
public void encodeURLExistingQueryExistingAliasMiddle() {
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "2")).isEqualTo("/url?a=b&_s=2&y=z");
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "2"))
.isEqualTo("/url?a=b&_s=2&y=z");
}
@Test
public void encodeURLExistingQueryExistingAliasEnd() {
assertThat(this.strategy.encodeURL("/url?a=b&_s=1", "2")).isEqualTo("/url?a=b&_s=2");
assertThat(this.strategy.encodeURL("/url?a=b&_s=1", "2"))
.isEqualTo("/url?a=b&_s=2");
}
//
@Test
public void encodeURLExistingQueryParamEndsWithActualParamStart() {
assertThat(this.strategy.encodeURL("/url?x_s=1&y=z", "2")).isEqualTo("/url?x_s=1&y=z&_s=2");
assertThat(this.strategy.encodeURL("/url?x_s=1&y=z", "2"))
.isEqualTo("/url?x_s=1&y=z&_s=2");
}
@Test
public void encodeURLExistingQueryParamEndsWithActualParamMiddle() {
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1&y=z", "2")).isEqualTo("/url?a=b&x_s=1&y=z&_s=2");
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1&y=z", "2"))
.isEqualTo("/url?a=b&x_s=1&y=z&_s=2");
}
@Test
public void encodeURLExistingQueryParamEndsWithActualParamEnd() {
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1", "2")).isEqualTo("/url?a=b&x_s=1&_s=2");
assertThat(this.strategy.encodeURL("/url?a=b&x_s=1", "2"))
.isEqualTo("/url?a=b&x_s=1&_s=2");
}
//
@@ -246,13 +261,16 @@ public class CookieHttpSessionStrategyTests {
@Test
public void encodeURLExistingQueryExistingAliasStartDefaultAlias() {
// relaxed constraint as result /url?&y=z does not hurt anything (ideally should remove the &)
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "0")).doesNotContain("_s=0&_s=1");
// relaxed constraint as result /url?&y=z does not hurt anything (ideally should
// remove the &)
assertThat(this.strategy.encodeURL("/url?_s=1&y=z", "0"))
.doesNotContain("_s=0&_s=1");
}
@Test
public void encodeURLExistingQueryExistingAliasMiddleDefaultAlias() {
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "0")).isEqualTo("/url?a=b&y=z");
assertThat(this.strategy.encodeURL("/url?a=b&_s=1&y=z", "0"))
.isEqualTo("/url?a=b&y=z");
}
@Test
@@ -262,88 +280,114 @@ public class CookieHttpSessionStrategyTests {
@Test
public void encodeURLMaliciousAlias() {
assertThat(this.strategy.encodeURL("/url?a=b&_s=1", "\"> <script>alert('hi')</script>")).isEqualTo("/url?a=b&_s=%22%3E+%3Cscript%3Ealert%28%27hi%27%29%3C%2Fscript%3E");
assertThat(this.strategy.encodeURL("/url?a=b&_s=1",
"\"> <script>alert('hi')</script>")).isEqualTo(
"/url?a=b&_s=%22%3E+%3Cscript%3Ealert%28%27hi%27%29%3C%2Fscript%3E");
}
// --- getCurrentSessionAlias
@Test
public void getCurrentSessionAliasNull() {
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasNullParamName() {
this.strategy.setSessionAliasParamName(null);
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "NOT USED");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "NOT USED");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
// protect against malicious users
@Test
public void getCurrentSessionAliasContainsQuote() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here\"this");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here\"this");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasContainsSingleQuote() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here'this");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here'this");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasContainsSpace() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here this");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here this");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasContainsLt() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here<this");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here<this");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasContainsGt() {
this.strategy.setSessionAliasParamName(null);
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here>this");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "here>this");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
public void getCurrentSessionAliasTooLong() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "012345678901234567890123456789012345678901234567890");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
"012345678901234567890123456789012345678901234567890");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
// We want some sort of length restrictions, but want to ensure some sort of length Technically no hard limit, but chose 50
// We want some sort of length restrictions, but want to ensure some sort of length
// Technically no hard limit, but chose 50
@Test
public void getCurrentSessionAliasAllows50() {
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, "01234567890123456789012345678901234567890123456789");
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
"01234567890123456789012345678901234567890123456789");
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo("01234567890123456789012345678901234567890123456789");
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo("01234567890123456789012345678901234567890123456789");
}
@Test
public void getCurrentSession() {
String expectedAlias = "1";
this.request.setParameter(CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME, expectedAlias);
assertThat(this.strategy.getCurrentSessionAlias(this.request)).isEqualTo(expectedAlias);
this.request.setParameter(
CookieHttpSessionStrategy.DEFAULT_SESSION_ALIAS_PARAM_NAME,
expectedAlias);
assertThat(this.strategy.getCurrentSessionAlias(this.request))
.isEqualTo(expectedAlias);
}
// --- getNewSessionAlias
@Test
public void getNewSessionAliasNoSessions() {
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
assertThat(this.strategy.getNewSessionAlias(this.request))
.isEqualTo(CookieHttpSessionStrategy.DEFAULT_ALIAS);
}
@Test
@@ -364,28 +408,32 @@ public class CookieHttpSessionStrategyTests {
public void getNewSessionAlias9Sessions() {
setCookieWithNSessions(9);
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualToIgnoringCase("9");
assertThat(this.strategy.getNewSessionAlias(this.request))
.isEqualToIgnoringCase("9");
}
@Test
public void getNewSessionAlias10Sessions() {
setCookieWithNSessions(10);
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualToIgnoringCase("a");
assertThat(this.strategy.getNewSessionAlias(this.request))
.isEqualToIgnoringCase("a");
}
@Test
public void getNewSessionAlias16Sessions() {
setCookieWithNSessions(16);
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualToIgnoringCase("10");
assertThat(this.strategy.getNewSessionAlias(this.request))
.isEqualToIgnoringCase("10");
}
@Test
public void getNewSessionAliasInvalidAlias() {
setSessionCookie("0 1 $ b");
assertThat(this.strategy.getNewSessionAlias(this.request)).isEqualToIgnoringCase("1");
assertThat(this.strategy.getNewSessionAlias(this.request))
.isEqualToIgnoringCase("1");
}
// --- getSessionIds
@@ -429,7 +477,8 @@ public class CookieHttpSessionStrategyTests {
@Test
public void createSessionCookieValue() {
assertThat(createSessionCookieValue(17)).isEqualToIgnoringCase("0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16");
assertThat(createSessionCookieValue(17)).isEqualToIgnoringCase(
"0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16");
}
private void setCookieWithNSessions(long size) {

View File

@@ -63,31 +63,38 @@ public class DefaultCookieSerializerTests {
public void readCookieValuesSingle() {
this.request.setCookies(new Cookie(this.cookieName, this.sessionId));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
assertThat(this.serializer.readCookieValues(this.request))
.containsOnly(this.sessionId);
}
@Test
public void readCookieValuesSingleAndInvalidName() {
this.request.setCookies(new Cookie(this.cookieName, this.sessionId), new Cookie(this.cookieName + "INVALID", this.sessionId + "INVALID"));
this.request.setCookies(new Cookie(this.cookieName, this.sessionId),
new Cookie(this.cookieName + "INVALID", this.sessionId + "INVALID"));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
assertThat(this.serializer.readCookieValues(this.request))
.containsOnly(this.sessionId);
}
@Test
public void readCookieValuesMulti() {
String secondSession = "secondSessionId";
this.request.setCookies(new Cookie(this.cookieName, this.sessionId), new Cookie(this.cookieName, secondSession));
this.request.setCookies(new Cookie(this.cookieName, this.sessionId),
new Cookie(this.cookieName, secondSession));
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
assertThat(this.serializer.readCookieValues(this.request))
.containsExactly(this.sessionId, secondSession);
}
@Test
public void readCookieValuesMultiCustomSessionCookieName() {
setCookieName("JSESSIONID");
String secondSession = "secondSessionId";
this.request.setCookies(new Cookie(this.cookieName, this.sessionId), new Cookie(this.cookieName, secondSession));
this.request.setCookies(new Cookie(this.cookieName, this.sessionId),
new Cookie(this.cookieName, secondSession));
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
assertThat(this.serializer.readCookieValues(this.request))
.containsExactly(this.sessionId, secondSession);
}
// gh-392
@@ -109,9 +116,11 @@ public class DefaultCookieSerializerTests {
@Test
public void readCookieValuesNullCookieValueAndNotNullCookie() {
this.serializer.setJvmRoute("123");
this.request.setCookies(new Cookie(this.cookieName, null), new Cookie(this.cookieName, this.sessionId));
this.request.setCookies(new Cookie(this.cookieName, null),
new Cookie(this.cookieName, this.sessionId));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
assertThat(this.serializer.readCookieValues(this.request))
.containsOnly(this.sessionId);
}
// --- writeCookie ---
@@ -182,7 +191,7 @@ public class DefaultCookieSerializerTests {
String domainNamePattern = "^.+?\\.(\\w+\\.[a-z]+)$";
this.serializer.setDomainNamePattern(domainNamePattern);
String[] matchingDomains = {"child.sub.example.com", "www.example.com"};
String[] matchingDomains = { "child.sub.example.com", "www.example.com" };
for (String domain : matchingDomains) {
this.request.setServerName(domain);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
@@ -191,7 +200,7 @@ public class DefaultCookieSerializerTests {
this.response = new MockHttpServletResponse();
}
String[] notMatchingDomains = {"example.com", "localhost", "127.0.0.1"};
String[] notMatchingDomains = { "example.com", "localhost", "127.0.0.1" };
for (String domain : notMatchingDomains) {
this.request.setServerName(domain);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
@@ -361,9 +370,11 @@ public class DefaultCookieSerializerTests {
public void readCookieJvmRoute() {
String jvmRoute = "route";
this.serializer.setJvmRoute(jvmRoute);
this.request.setCookies(new Cookie(this.cookieName, this.sessionId + "." + jvmRoute));
this.request
.setCookies(new Cookie(this.cookieName, this.sessionId + "." + jvmRoute));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
assertThat(this.serializer.readCookieValues(this.request))
.containsOnly(this.sessionId);
}
@Test
@@ -372,7 +383,8 @@ public class DefaultCookieSerializerTests {
this.serializer.setJvmRoute(jvmRoute);
this.request.setCookies(new Cookie(this.cookieName, this.sessionId));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
assertThat(this.serializer.readCookieValues(this.request))
.containsOnly(this.sessionId);
}
@Test

View File

@@ -51,14 +51,16 @@ public class HeaderSessionStrategyTests {
@Test
public void getRequestedSessionIdNotNull() throws Exception {
setSessionId(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request)).isEqualTo(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request))
.isEqualTo(this.session.getId());
}
@Test
public void getRequestedSessionIdNotNullCustomHeaderName() throws Exception {
setHeaderName("CUSTOM");
setSessionId(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request)).isEqualTo(this.session.getId());
assertThat(this.strategy.getRequestedSessionId(this.request))
.isEqualTo(this.session.getId());
}
@Test
@@ -74,7 +76,8 @@ public class HeaderSessionStrategyTests {
this.strategy.onNewSession(this.session, this.request, this.response);
assertThat(this.response.getHeaders(this.headerName).size()).isEqualTo(1);
assertThat(this.response.getHeaders(this.headerName)).containsOnly(this.session.getId());
assertThat(this.response.getHeaders(this.headerName))
.containsOnly(this.session.getId());
}
@Test
@@ -90,7 +93,6 @@ public class HeaderSessionStrategyTests {
assertThat(getSessionId()).isEmpty();
}
// the header is set as apposed to added
@Test
public void onDeleteSessionMulti() throws Exception {

View File

@@ -60,7 +60,6 @@ public class OnCommittedResponseWrapperTests {
given(this.delegate.getOutputStream()).willReturn(this.out);
}
// --- printwriter
@Test
@@ -338,7 +337,6 @@ public class OnCommittedResponseWrapperTests {
verify(this.writer).format(l, format, args);
}
@Test
public void printWriterAppendCharSequence() throws Exception {
String x = "a";
@@ -359,7 +357,6 @@ public class OnCommittedResponseWrapperTests {
verify(this.writer).append(x, start, end);
}
@Test
public void printWriterAppendChar() throws Exception {
char x = 1;
@@ -371,7 +368,6 @@ public class OnCommittedResponseWrapperTests {
// servletoutputstream
@Test
public void outputStreamHashCode() throws Exception {
int expected = this.out.hashCode();
@@ -565,7 +561,8 @@ public class OnCommittedResponseWrapperTests {
}
@Test
public void contentLengthPlus1PrintWriterWriteIntMultiDigitCommits() throws Exception {
public void contentLengthPlus1PrintWriterWriteIntMultiDigitCommits()
throws Exception {
int expected = 10000;
this.response.setContentLength(String.valueOf(expected).length() + 1);
@@ -578,7 +575,6 @@ public class OnCommittedResponseWrapperTests {
assertThat(this.committed).isTrue();
}
@Test
public void contentLengthPrintWriterWriteCharIntIntCommits() throws Exception {
char[] buff = new char[0];
@@ -613,7 +609,6 @@ public class OnCommittedResponseWrapperTests {
assertThat(this.committed).isTrue();
}
@Test
public void contentLengthPrintWriterWriteStringCommits() throws IOException {
String body = "something";
@@ -843,7 +838,8 @@ public class OnCommittedResponseWrapperTests {
}
@Test
public void contentLengthPrintWriterAppendCharSequenceIntIntCommits() throws Exception {
public void contentLengthPrintWriterAppendCharSequenceIntIntCommits()
throws Exception {
String x = "abcdef";
int start = 1;
int end = 3;
@@ -885,7 +881,8 @@ public class OnCommittedResponseWrapperTests {
}
@Test
public void contentLengthPlus1OutputStreamWriteIntMultiDigitCommits() throws Exception {
public void contentLengthPlus1OutputStreamWriteIntMultiDigitCommits()
throws Exception {
int expected = 10000;
this.response.setContentLength(String.valueOf(expected).length() + 1);
@@ -900,12 +897,11 @@ public class OnCommittedResponseWrapperTests {
// gh-171
@Test
public void contentLengthPlus1OutputStreamWriteByteArrayMultiDigitCommits() throws Exception {
String expected = "{\n" +
" \"parameterName\" : \"_csrf\",\n" +
" \"token\" : \"06300b65-c4aa-4c8f-8cda-39ee17f545a0\",\n" +
" \"headerName\" : \"X-CSRF-TOKEN\"\n" +
"}";
public void contentLengthPlus1OutputStreamWriteByteArrayMultiDigitCommits()
throws Exception {
String expected = "{\n" + " \"parameterName\" : \"_csrf\",\n"
+ " \"token\" : \"06300b65-c4aa-4c8f-8cda-39ee17f545a0\",\n"
+ " \"headerName\" : \"X-CSRF-TOKEN\"\n" + "}";
this.response.setContentLength(expected.length() + 1);
this.response.getOutputStream().write(expected.getBytes());
@@ -1088,7 +1084,8 @@ public class OnCommittedResponseWrapperTests {
@Test
public void addHeaderContentLengthPrintWriterWriteStringCommits() throws Exception {
int expected = 1234;
this.response.addHeader("Content-Length", String.valueOf(String.valueOf(expected).length()));
this.response.addHeader("Content-Length",
String.valueOf(String.valueOf(expected).length()));
this.response.getWriter().write(expected);

Some files were not shown because too many files have changed in this diff Show More