Additional Checkstyle Fixes
Issue gh-393
This commit is contained in:
@@ -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")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:<redisNamespace>:". 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Session> repo =
|
||||
* (SessionRepository<Session>) req.getAttribute(SessionRepository.class.getName());
|
||||
*
|
||||
* String currentSessionAlias = sessionManager.getCurrentSessionAlias(req);
|
||||
* Map<String, String> sessionIds = sessionManager.getSessionIds(req);
|
||||
* Map<String, String> 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<Account> accounts = new ArrayList<>();
|
||||
* Account currentAccount = null; for(Map.Entry<String, String> 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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user