Compare commits

..

9 Commits

Author SHA1 Message Date
Rob Winch
798d398d9b Release 2.2.0.RELEASE 2019-10-15 15:31:21 -05:00
Vedran Pavic
085554f56b Polish DefaultCookieSerializer
See: #1514
2019-10-09 08:58:42 +02:00
Vedran Pavic
45b3b35db7 Update Travis CI config 2019-10-08 12:28:42 +02:00
Vedran Pavic
2d06e1159c Improve Hazelcast integration tests
Resolves: #1534
2019-10-08 12:04:42 +02:00
Vedran Pavic
927008bdc8 Ensure session cookie's expires directive uses GMT format
Resolves: #1514
2019-10-07 21:54:00 +02:00
Vedran Pavic
30588dc3c8 Improve Hazelcast client-server topology integration tests
Resolves: #1527
2019-10-06 15:54:02 +02:00
Vedran Pavic
2f79da00dc Upgrade Hazelcast to 3.12.3
Resolves: #1525
2019-10-06 11:41:03 +02:00
Vedran Pavic
e2abe36fa8 Upgrade samples to Spring Boot 2.2.0.RC1
Resolves: #1521
2019-10-03 17:16:59 +02:00
Vedran Pavic
456fd3adb4 Next development version 2019-10-01 06:26:41 +02:00
19 changed files with 93 additions and 438 deletions

View File

@@ -1,20 +1,16 @@
language: java
sudo: required
services: docker
jdk: oraclejdk8
language: java
jdk:
- openjdk8
- openjdk11
services:
- docker
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install: true
script: ./gradlew clean check --no-daemon --stacktrace

View File

@@ -4,7 +4,7 @@ buildscript {
snapshotBuild = version.endsWith('SNAPSHOT')
milestoneBuild = !(releaseBuild || snapshotBuild)
springBootVersion = '2.2.0.M6'
springBootVersion = '2.2.0.RC1'
}
repositories {

View File

@@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.parallel=true
version=2.2.0.RC1
version=2.2.0.RELEASE

View File

@@ -5,11 +5,11 @@ dependencyManagement {
mavenBom 'org.springframework:spring-framework-bom:5.2.0.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Moore-RELEASE'
mavenBom 'org.springframework.security:spring-security-bom:5.2.0.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.12.0'
mavenBom 'org.testcontainers:testcontainers-bom:1.12.2'
}
dependencies {
dependencySet(group: 'com.hazelcast', version: '3.12.2') {
dependencySet(group: 'com.hazelcast', version: '3.12.3') {
entry 'hazelcast'
entry 'hazelcast-client'
}

View File

@@ -16,9 +16,10 @@
package org.springframework.session.web.http;
import java.time.Clock;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
@@ -62,6 +63,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
domainValid.set('-');
}
private Clock clock = Clock.systemUTC();
private String cookieName = "SESSION";
private Boolean useSecureCookie;
@@ -121,7 +124,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
public void writeCookieValue(CookieValue cookieValue) {
HttpServletRequest request = cookieValue.getRequest();
HttpServletResponse response = cookieValue.getResponse();
StringBuilder sb = new StringBuilder();
sb.append(this.cookieName).append('=');
String value = getValue(cookieValue);
@@ -132,8 +134,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
int maxAge = getMaxAge(cookieValue);
if (maxAge > -1) {
sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
OffsetDateTime expires = (maxAge != 0) ? OffsetDateTime.now().plusSeconds(maxAge)
: Instant.EPOCH.atOffset(ZoneOffset.UTC);
ZonedDateTime expires = (maxAge != 0) ? ZonedDateTime.now(this.clock).plusSeconds(maxAge)
: Instant.EPOCH.atZone(ZoneOffset.UTC);
sb.append("; Expires=").append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
}
String domain = getDomainName(request);
@@ -155,7 +157,6 @@ public class DefaultCookieSerializer implements CookieSerializer {
if (this.sameSite != null) {
sb.append("; SameSite=").append(this.sameSite);
}
response.addHeader("Set-Cookie", sb.toString());
}
@@ -259,6 +260,10 @@ public class DefaultCookieSerializer implements CookieSerializer {
}
}
void setClock(Clock clock) {
this.clock = clock.withZone(ZoneOffset.UTC);
}
/**
* Sets if a Cookie marked as secure should be used. The default is to use the value
* of {@link HttpServletRequest#isSecure()}.

View File

@@ -16,6 +16,9 @@
package org.springframework.session.web.http;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Base64;
import javax.servlet.http.Cookie;
@@ -25,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockCookie;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -48,7 +52,7 @@ class DefaultCookieSerializerTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private CookiePreservingMockHttpServletResponse response;
private DefaultCookieSerializer serializer;
@@ -58,7 +62,7 @@ class DefaultCookieSerializerTests {
void setup() {
this.cookieName = "SESSION";
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.response = new CookiePreservingMockHttpServletResponse();
this.sessionId = "sessionId";
this.serializer = new DefaultCookieSerializer();
}
@@ -71,11 +75,10 @@ class DefaultCookieSerializerTests {
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesSingle(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
}
@@ -84,81 +87,73 @@ class DefaultCookieSerializerTests {
this.sessionId = "&^%$*";
this.serializer.setUseBase64Encoding(true);
this.request.setCookies(new Cookie(this.cookieName, this.sessionId));
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesSingleAndInvalidName(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
createCookie(this.cookieName + "INVALID", this.sessionId + "INVALID", useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesMulti(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
String secondSession = "secondSessionId";
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
createCookie(this.cookieName, secondSession, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesMultiCustomSessionCookieName(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
setCookieName("JSESSIONID");
String secondSession = "secondSessionId";
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding),
createCookie(this.cookieName, secondSession, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsExactly(this.sessionId, secondSession);
}
// gh-392
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesNullCookieValue(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesNullCookieValueAndJvmRoute(boolean useBase64Encoding) {
this.serializer.setJvmRoute("123");
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).isEmpty();
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieValuesNullCookieValueAndNotNullCookie(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
this.serializer.setJvmRoute("123");
this.request.setCookies(createCookie(this.cookieName, null, useBase64Encoding),
createCookie(this.cookieName, this.sessionId, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
}
// --- writeCookie ---
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void writeCookie(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookieValue(useBase64Encoding)).isEqualTo(this.sessionId);
}
@@ -167,25 +162,20 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieHttpOnlyDefault() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().isHttpOnly()).isTrue();
}
@Test
void writeCookieHttpOnlySetTrue() {
this.serializer.setUseHttpOnlyCookie(true);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().isHttpOnly()).isTrue();
}
@Test
void writeCookieHttpOnlySetFalse() {
this.serializer.setUseHttpOnlyCookie(false);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().isHttpOnly()).isFalse();
}
@@ -194,7 +184,6 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieDomainNameDefault() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getDomain()).isNull();
}
@@ -202,9 +191,7 @@ class DefaultCookieSerializerTests {
void writeCookieDomainNameCustom() {
String domainName = "example.com";
this.serializer.setDomainName(domainName);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getDomain()).isEqualTo(domainName);
}
@@ -221,23 +208,19 @@ class DefaultCookieSerializerTests {
void writeCookieDomainNamePattern() {
String domainNamePattern = "^.+?\\.(\\w+\\.[a-z]+)$";
this.serializer.setDomainNamePattern(domainNamePattern);
String[] matchingDomains = { "child.sub.example.com", "www.example.com" };
for (String domain : matchingDomains) {
this.request.setServerName(domain);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getDomain()).isEqualTo("example.com");
this.response = new MockHttpServletResponse();
this.response = new CookiePreservingMockHttpServletResponse();
}
String[] notMatchingDomains = { "example.com", "localhost", "127.0.0.1" };
for (String domain : notMatchingDomains) {
this.request.setServerName(domain);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getDomain()).isNull();
this.response = new MockHttpServletResponse();
this.response = new CookiePreservingMockHttpServletResponse();
}
}
@@ -253,7 +236,6 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieCookieNameDefault() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getName()).isEqualTo("SESSION");
}
@@ -261,9 +243,7 @@ class DefaultCookieSerializerTests {
void writeCookieCookieNameCustom() {
String cookieName = "JSESSIONID";
setCookieName(cookieName);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getName()).isEqualTo(cookieName);
}
@@ -278,18 +258,14 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieCookiePathDefaultEmptyContextPathUsed() {
this.request.setContextPath("");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getPath()).isEqualTo("/");
}
@Test
void writeCookieCookiePathDefaultContextPathUsed() {
this.request.setContextPath("/context");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getPath()).isEqualTo("/context/");
}
@@ -297,9 +273,7 @@ class DefaultCookieSerializerTests {
void writeCookieCookiePathExplicitNullCookiePathContextPathUsed() {
this.request.setContextPath("/context");
this.serializer.setCookiePath(null);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getPath()).isEqualTo("/context/");
}
@@ -307,9 +281,7 @@ class DefaultCookieSerializerTests {
void writeCookieCookiePathExplicitCookiePath() {
this.request.setContextPath("/context");
this.serializer.setCookiePath("/");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getPath()).isEqualTo("/");
}
@@ -318,36 +290,36 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieCookieMaxAgeDefault() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getMaxAge()).isEqualTo(-1);
assertThat(this.response.rawCookie).doesNotContain("Expires");
}
@Test
void writeCookieCookieMaxAgeExplicit() {
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
this.serializer.setCookieMaxAge(100);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getMaxAge()).isEqualTo(100);
assertThat(this.response.rawCookie).contains("Expires=Mon, 7 Oct 2019 20:11:40 GMT");
}
@Test
void writeCookieCookieMaxAgeExplicitEmptyCookie() {
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
this.serializer.setCookieMaxAge(100);
this.serializer.writeCookieValue(cookieValue(""));
assertThat(getCookie().getMaxAge()).isEqualTo(0);
assertThat(this.response.rawCookie).contains("Expires=Thu, 1 Jan 1970 00:00:00 GMT");
}
@Test
void writeCookieCookieMaxAgeExplicitCookieValue() {
this.serializer.setClock(Clock.fixed(Instant.parse("2019-10-07T20:10:00Z"), ZoneOffset.UTC));
CookieValue cookieValue = cookieValue(this.sessionId);
cookieValue.setCookieMaxAge(100);
this.serializer.writeCookieValue(cookieValue);
assertThat(getCookie().getMaxAge()).isEqualTo(100);
assertThat(this.response.rawCookie).contains("Expires=Mon, 7 Oct 2019 20:11:40 GMT");
}
// --- secure ---
@@ -355,7 +327,6 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieDefaultInsecureRequest() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSecure()).isFalse();
}
@@ -363,18 +334,14 @@ class DefaultCookieSerializerTests {
void writeCookieSecureSecureRequest() {
this.request.setSecure(true);
this.serializer.setUseSecureCookie(true);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSecure()).isTrue();
}
@Test
void writeCookieSecureInsecureRequest() {
this.serializer.setUseSecureCookie(true);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSecure()).isTrue();
}
@@ -382,65 +349,56 @@ class DefaultCookieSerializerTests {
void writeCookieInsecureSecureRequest() {
this.request.setSecure(true);
this.serializer.setUseSecureCookie(false);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSecure()).isFalse();
}
@Test
void writeCookieInecureInsecureRequest() {
this.serializer.setUseSecureCookie(false);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSecure()).isFalse();
}
// --- jvmRoute ---
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void writeCookieJvmRoute(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
String jvmRoute = "route";
this.serializer.setJvmRoute(jvmRoute);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookieValue(useBase64Encoding)).isEqualTo(this.sessionId + "." + jvmRoute);
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieJvmRoute(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
String jvmRoute = "route";
this.serializer.setJvmRoute(jvmRoute);
this.request.setCookies(createCookie(this.cookieName, this.sessionId + "." + jvmRoute, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieJvmRouteRouteMissing(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
String jvmRoute = "route";
this.serializer.setJvmRoute(jvmRoute);
this.request.setCookies(createCookie(this.cookieName, this.sessionId, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly(this.sessionId);
}
@ParameterizedTest
@ValueSource(strings = { "true", "false" })
@ValueSource(booleans = { true, false })
void readCookieJvmRouteOnlyRoute(boolean useBase64Encoding) {
this.serializer.setUseBase64Encoding(useBase64Encoding);
String jvmRoute = "route";
this.serializer.setJvmRoute(jvmRoute);
this.request.setCookies(createCookie(this.cookieName, "." + jvmRoute, useBase64Encoding));
assertThat(this.serializer.readCookieValues(this.request)).containsOnly("");
}
@@ -451,7 +409,6 @@ class DefaultCookieSerializerTests {
this.request.setAttribute("rememberMe", true);
this.serializer.setRememberMeRequestAttribute("rememberMe");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getMaxAge()).isEqualTo(Integer.MAX_VALUE);
}
@@ -462,7 +419,6 @@ class DefaultCookieSerializerTests {
CookieValue cookieValue = cookieValue(this.sessionId);
cookieValue.setCookieMaxAge(100);
this.serializer.writeCookieValue(cookieValue);
assertThat(getCookie().getMaxAge()).isEqualTo(100);
}
@@ -471,7 +427,6 @@ class DefaultCookieSerializerTests {
@Test
void writeCookieDefaultSameSiteLax() {
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
}
@@ -479,7 +434,6 @@ class DefaultCookieSerializerTests {
void writeCookieSetSameSiteLax() {
this.serializer.setSameSite("Lax");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Lax");
}
@@ -487,7 +441,6 @@ class DefaultCookieSerializerTests {
void writeCookieSetSameSiteStrict() {
this.serializer.setSameSite("Strict");
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isEqualTo("Strict");
}
@@ -495,7 +448,6 @@ class DefaultCookieSerializerTests {
void writeCookieSetSameSiteNull() {
this.serializer.setSameSite(null);
this.serializer.writeCookieValue(cookieValue(this.sessionId));
assertThat(getCookie().getSameSite()).isNull();
}
@@ -530,4 +482,18 @@ class DefaultCookieSerializerTests {
return new CookieValue(this.request, this.response, cookieValue);
}
private static class CookiePreservingMockHttpServletResponse extends MockHttpServletResponse {
private String rawCookie;
@Override
public void addHeader(String name, String value) {
if (HttpHeaders.SET_COOKIE.equals(name)) {
this.rawCookie = value;
}
super.addHeader(name, value);
}
}
}

View File

@@ -35,16 +35,13 @@ public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) // <2>
.addMapAttributeConfig(attributeConfig).addMapIndexConfig(
new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config); // <3>
}

View File

@@ -22,8 +22,8 @@ import com.hazelcast.core.HazelcastInstance;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.MountableFile;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -46,10 +46,9 @@ import org.springframework.test.context.web.WebAppConfiguration;
@WebAppConfiguration
class ClientServerHazelcastIndexedSessionRepositoryITests extends AbstractHazelcastIndexedSessionRepositoryITests {
private static GenericContainer container = new GenericContainer<>("hazelcast/hazelcast:3.12.2")
.withExposedPorts(5701).withEnv("JAVA_OPTS", "-Dhazelcast.config=/opt/hazelcast/config_ext/hazelcast.xml")
.withClasspathResourceMapping("/hazelcast-server.xml", "/opt/hazelcast/config_ext/hazelcast.xml",
BindMode.READ_ONLY);
private static GenericContainer container = new GenericContainer<>("hazelcast/hazelcast:3.12.3")
.withExposedPorts(5701).withCopyFileToContainer(MountableFile.forClasspathResource("/hazelcast-server.xml"),
"/opt/hazelcast/hazelcast.xml");
@BeforeAll
static void setUpClass() {

View File

@@ -23,49 +23,32 @@ import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.util.SocketUtils;
/**
* Utility class for Hazelcast integration tests.
*
* @author Vedran Pavic
*/
public final class HazelcastITestUtils {
final class HazelcastITestUtils {
private HazelcastITestUtils() {
}
/**
* Creates {@link HazelcastInstance} for use in integration tests.
* @param port the port for Hazelcast to bind to
* @return the Hazelcast instance
*/
public static HazelcastInstance embeddedHazelcastServer(int port) {
static HazelcastInstance embeddedHazelcastServer() {
Config config = new Config();
NetworkConfig networkConfig = config.getNetworkConfig();
networkConfig.setPort(0);
networkConfig.getJoin().getMulticastConfig().setEnabled(false);
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
NetworkConfig networkConfig = config.getNetworkConfig();
networkConfig.setPort(port);
networkConfig.getJoin().getMulticastConfig().setEnabled(false);
config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addMapAttributeConfig(attributeConfig).addMapIndexConfig(
new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config);
}
/**
* Creates {@link HazelcastInstance} for use in integration tests.
* @return the Hazelcast instance
*/
public static HazelcastInstance embeddedHazelcastServer() {
return embeddedHazelcastServer(SocketUtils.findAvailableTcpPort());
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.session.hazelcast.config.annotation.web.http;
package org.springframework.session.hazelcast;
import java.time.Duration;
import java.time.Instant;
@@ -38,8 +38,7 @@ import org.springframework.session.SessionRepository;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.session.hazelcast.HazelcastITestUtils;
import org.springframework.session.hazelcast.SessionEventRegistry;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
@@ -57,7 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@ContextConfiguration
@WebAppConfiguration
class EnableHazelcastHttpSessionEventsTests<S extends Session> {
class SessionEventHazelcastIndexedSessionRepositoryTests<S extends Session> {
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;

View File

@@ -24,7 +24,7 @@ import java.util.concurrent.ConcurrentMap;
import org.springframework.context.ApplicationListener;
import org.springframework.session.events.AbstractSessionEvent;
public class SessionEventRegistry implements ApplicationListener<AbstractSessionEvent> {
class SessionEventRegistry implements ApplicationListener<AbstractSessionEvent> {
private Map<String, AbstractSessionEvent> events = new HashMap<>();
@@ -40,17 +40,17 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
}
}
public void clear() {
void clear() {
this.events.clear();
this.locks.clear();
}
public boolean receivedEvent(String sessionId) throws InterruptedException {
boolean receivedEvent(String sessionId) throws InterruptedException {
return waitForEvent(sessionId) != null;
}
@SuppressWarnings("unchecked")
public <E extends AbstractSessionEvent> E getEvent(String sessionId) throws InterruptedException {
<E extends AbstractSessionEvent> E getEvent(String sessionId) throws InterruptedException {
return (E) waitForEvent(sessionId);
}

View File

@@ -1,127 +0,0 @@
/*
* Copyright 2014-2019 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
*
* https://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.hazelcast.config.annotation.web.http;
import java.time.Duration;
import com.hazelcast.config.ClasspathXmlConfig;
import com.hazelcast.config.Config;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test the different configuration options for the {@link EnableHazelcastHttpSession}
* annotation.
*
* @author Tommy Ludwig
*/
public class HazelcastHttpSessionConfigurationXmlTests<S extends Session> {
@ExtendWith(SpringExtension.class)
@ContextConfiguration
@WebAppConfiguration
static class CustomXmlMapNameTest<S extends Session> {
@Autowired
private SessionRepository<S> repository;
@Test
void saveSessionTest() {
S sessionToSave = this.repository.createSession();
this.repository.save(sessionToSave);
S session = this.repository.findById(sessionToSave.getId());
assertThat(session.getId()).isEqualTo(sessionToSave.getId());
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(30));
}
@Configuration
@EnableHazelcastHttpSession(sessionMapName = "my-sessions")
static class HazelcastSessionXmlConfigCustomMapName {
@Bean
HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new ClasspathXmlConfig(
"org/springframework/session/hazelcast/config/annotation/web/http/hazelcast-custom-map-name.xml");
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
hazelcastConfig.setNetworkConfig(netConfig);
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
}
}
@ExtendWith(SpringExtension.class)
@ContextConfiguration
@WebAppConfiguration
static class CustomXmlMapNameAndIdleTest<S extends Session> {
@Autowired
private SessionRepository<S> repository;
@Test
void saveSessionTest() {
S sessionToSave = this.repository.createSession();
this.repository.save(sessionToSave);
S session = this.repository.findById(sessionToSave.getId());
assertThat(session.getId()).isEqualTo(sessionToSave.getId());
assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(20));
}
@Configuration
@EnableHazelcastHttpSession(sessionMapName = "test-sessions", maxInactiveIntervalInSeconds = 1200)
static class HazelcastSessionXmlConfigCustomMapNameAndIdle {
@Bean
HazelcastInstance embeddedHazelcast() {
Config hazelcastConfig = new ClasspathXmlConfig(
"org/springframework/session/hazelcast/config/annotation/web/http/hazelcast-custom-idle-time-map-name.xml");
NetworkConfig netConfig = new NetworkConfig();
netConfig.setPort(SocketUtils.findAvailableTcpPort());
hazelcastConfig.setNetworkConfig(netConfig);
return Hazelcast.newHazelcastInstance(hazelcastConfig);
}
}
}
}

View File

@@ -3,6 +3,12 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/config https://www.hazelcast.com/schema/config/hazelcast-config-3.12.xsd">
<network>
<join>
<multicast enabled="false"/>
</join>
</network>
<user-code-deployment enabled="true">
<class-cache-mode>ETERNAL</class-cache-mode>
<provider-mode>LOCAL_AND_CACHED_CLASSES</provider-mode>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/config https://www.hazelcast.com/schema/config/hazelcast-config-3.12.xsd">
<group>
<name>spring-session-it-test-idle-time-map-name</name>
<password>test-pass</password>
</group>
<network>
<port auto-increment="true" port-count="100">5701</port>
<outbound-ports>
<ports>0</ports>
</outbound-ports>
<join>
<multicast enabled="false"/>
</join>
</network>
<map name="test-sessions">
<in-memory-format>BINARY</in-memory-format>
<backup-count>1</backup-count>
<async-backup-count>0</async-backup-count>
<time-to-live-seconds>0</time-to-live-seconds>
<max-idle-seconds>300</max-idle-seconds>
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy>
<attributes>
<attribute extractor="org.springframework.session.hazelcast.PrincipalNameExtractor">principalName</attribute>
</attributes>
<indexes>
<index ordered="false">principalName</index>
</indexes>
</map>
</hazelcast>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/config https://www.hazelcast.com/schema/config/hazelcast-config-3.12.xsd">
<group>
<name>spring-session-it-test-map-name</name>
<password>test-pass</password>
</group>
<network>
<port auto-increment="true" port-count="100">5701</port>
<outbound-ports>
<ports>0</ports>
</outbound-ports>
<join>
<multicast enabled="false"/>
</join>
</network>
<map name="my-sessions">
<in-memory-format>BINARY</in-memory-format>
<backup-count>1</backup-count>
<async-backup-count>0</async-backup-count>
<time-to-live-seconds>0</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
<merge-policy>com.hazelcast.map.merge.PutIfAbsentMapMergePolicy</merge-policy>
<attributes>
<attribute extractor="org.springframework.session.hazelcast.PrincipalNameExtractor">principalName</attribute>
</attributes>
<indexes>
<index ordered="false">principalName</index>
</indexes>
</map>
</hazelcast>

View File

@@ -1,10 +1,3 @@
ext['jackson.version'] = '2.10.0'
ext['reactor-bom.version'] = 'Dysprosium-RELEASE'
ext['spring-data-releasetrain.version'] = 'Moore-RELEASE'
ext['spring-framework.version'] = '5.2.0.RELEASE'
ext['spring-security.version'] = '5.2.0.RELEASE'
ext['webjars-locator-core.version'] = '0.38'
dependencyManagement {
imports {
mavenBom 'com.fasterxml.jackson:jackson-bom:2.10.0'

View File

@@ -1,67 +0,0 @@
/*
* Copyright 2014-2019 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
*
* https://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 sample;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.StreamSerializer;
/**
* A {@link StreamSerializer} that uses Java serialization to persist the session. This is
* certainly not the most efficient way to persist sessions, but the example is intended
* to demonstrate using minimal dependencies. For better serialization methods try using
* <a href="https://github.com/EsotericSoftware/kryo">Kryo</a>.
*
* @author Rob Winch
*
*/
public class ObjectStreamSerializer implements StreamSerializer<Object> {
@Override
public int getTypeId() {
return 2;
}
@Override
public void write(ObjectDataOutput objectDataOutput, Object object) throws IOException {
ObjectOutputStream out = new ObjectOutputStream((OutputStream) objectDataOutput);
out.writeObject(object);
out.flush();
}
@Override
public Object read(ObjectDataInput objectDataInput) throws IOException {
ObjectInputStream in = new ObjectInputStream((InputStream) objectDataInput);
try {
return in.readObject();
}
catch (ClassNotFoundException ex) {
throw new IOException(ex);
}
}
@Override
public void destroy() {
}
}

View File

@@ -19,7 +19,7 @@ package sample;
import com.hazelcast.config.Config;
import com.hazelcast.config.MapAttributeConfig;
import com.hazelcast.config.MapIndexConfig;
import com.hazelcast.config.SerializerConfig;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
@@ -28,7 +28,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository;
import org.springframework.session.hazelcast.PrincipalNameExtractor;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.util.SocketUtils;
// tag::class[]
@EnableHazelcastHttpSession(maxInactiveIntervalInSeconds = 300)
@@ -38,26 +37,15 @@ public class SessionConfig {
@Bean(destroyMethod = "shutdown")
public HazelcastInstance hazelcastInstance() {
Config config = new Config();
int port = SocketUtils.findAvailableTcpPort();
config.getNetworkConfig().setPort(port).getJoin().getMulticastConfig().setEnabled(false);
System.out.println("Hazelcast port #: " + port);
SerializerConfig serializer = new SerializerConfig().setImplementation(new ObjectStreamSerializer())
.setTypeClass(Object.class);
config.getSerializationConfig().addSerializerConfig(serializer);
NetworkConfig networkConfig = config.getNetworkConfig();
networkConfig.setPort(0);
networkConfig.getJoin().getMulticastConfig().setEnabled(false);
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME)
.addMapAttributeConfig(attributeConfig).addMapIndexConfig(
new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config);
}

View File

@@ -16,8 +16,6 @@
package sample;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.EnumSet;
import java.util.Map;
@@ -28,6 +26,7 @@ import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import com.hazelcast.config.Config;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
@@ -62,21 +61,11 @@ public class Initializer implements ServletContextListener {
private HazelcastInstance createHazelcastInstance() {
Config config = new Config();
config.getNetworkConfig().setPort(getAvailablePort()).getJoin().getMulticastConfig().setEnabled(false);
NetworkConfig networkConfig = config.getNetworkConfig();
networkConfig.setPort(0);
networkConfig.getJoin().getMulticastConfig().setEnabled(false);
config.getMapConfig(SESSION_MAP_NAME).setTimeToLiveSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
return Hazelcast.newHazelcastInstance(config);
}
private static int getAvailablePort() {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}