Compare commits

..

18 Commits

Author SHA1 Message Date
Vedran Pavic
d7c2e8e79c Release 2.0.5.RELEASE 2018-07-29 09:48:45 +02:00
Vedran Pavic
6bec95a298 Polish 2018-07-27 13:27:27 +02:00
Vedran Pavic
9249a140c9 Upgrade dependencies 2018-07-27 11:05:33 +02:00
Vedran Pavic
7f6dc801e0 Upgrade Spring Data to Kay-SR9
Closes gh-1122
2018-07-27 10:23:12 +02:00
Vedran Pavic
83d46ad685 Upgrade Spring Security to 5.0.7.RELEASE
Closes gh-1123
2018-07-27 01:14:21 +02:00
Vedran Pavic
21cef2b7fa Upgrade Spring Framework to 5.0.8.RELEASE
Closes gh-1121
2018-07-26 23:25:13 +02:00
Vedran Pavic
db31527c8c Add logging for errors decoding Base64 cookies
Closes gh-1117
2018-07-24 23:37:52 +02:00
Vedran Pavic
3d2a742328 Use Spring Java Format Checkstyle
Closes gh-1113
2018-07-23 15:16:35 +02:00
Vedran Pavic
7ac6e458e0 Update integration tests 2018-07-23 12:15:14 +02:00
Vedran Pavic
9adf0a6e0c Upgrade spring-build-conventions to 0.0.17.RELEASE 2018-07-18 09:38:02 +02:00
Vedran Pavic
58219fa016 Upgrade Gradle to 4.9 2018-07-18 08:15:38 +02:00
Vedran Pavic
83cbff5ce2 Improve support for Hazelcast client-server topology
This commit improves support for use of Spring Session with Hazelcast's client-server topology by ensuring SessionUpdateEntryProcessor is easier to serialize to the cluster. This is done by refactoring SessionUpdateEntryProcessor from static inner class of HazelcastSessionRepository to a dedicated class, therefore minimizing the dependencies to other Spring Session components.

Closes gh-1101
2018-07-17 21:42:33 +02:00
Vedran Pavic
936fc853df Ensure Session#getAttributeNames implementations return a copy
Currently, Session#getAttributeNames implementations, by delegating to MapSession, all return a session attribute map's key set. This causes ConcurrentModificationException when an attempt to modify session attributes is made while iterating over the returned attribute names.

Closes gh-1120
2018-07-17 15:05:03 +02:00
Vedran Pavic
dba475c48f Invalidate session before clearing session store
Closes gh-1114
2018-07-13 10:50:49 +02:00
Vedran Pavic
9956e91b93 Upgrade samples to Spring Boot 2.0.3.RELEASE
Closes gh-1107
2018-07-13 10:50:49 +02:00
Dave Syer
c902981eba Fix garbled syntax relating to dropped APIs 2018-07-11 08:07:58 -05:00
Vedran Pavic
2e26c6e9d3 Upgrade Gradle to 4.8.1 2018-06-21 22:53:20 +02:00
Vedran Pavic
b9cd3865c5 Next development version 2018-06-13 23:13:05 +02:00
70 changed files with 461 additions and 391 deletions

View File

@@ -1,6 +1,6 @@
buildscript {
dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.16.RELEASE'
classpath 'io.spring.gradle:spring-build-conventions:0.0.17.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
repositories {

View File

@@ -1200,7 +1200,7 @@ Note that these two have moved to separate repositories, and will continue to be
=== Dropped Support
As a part of the changes to `HttpSessionStrategy` and it's alignment to the counterpart from the reactive world, the support for managing multiple users' sessions in a single browser instance has been removed.
This introduction of new API to replace this functionality in consideration for future releases.
The introduction of a new API to replace this functionality is under consideration for future releases.
[[community]]
== Spring Session Community

View File

@@ -2,165 +2,11 @@
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- Suppressions -->
<!-- Supressions -->
<module name="SuppressionFilter">
<property name="file" value="${configDir}/suppressions.xml"/>
</module>
<!-- Root Checks -->
<module name="RegexpHeader">
<property name="headerFile" value="${configDir}/header.txt"/>
<property name="fileExtensions" value="java"/>
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
<property name="fileExtensions" value="java,xml"/>
</module>
<!-- TreeWalker Checks -->
<module name="TreeWalker">
<!-- Annotations -->
<module name="AnnotationUseStyle">
<property name="elementStyle" value="compact"/>
</module>
<module name="MissingOverride"/>
<module name="PackageAnnotation"/>
<module name="AnnotationLocation">
<property name="allowSamelineSingleParameterlessAnnotation" value="false" />
</module>
<!-- Block Checks -->
<module name="EmptyBlock">
<property name="option" value="text"/>
</module>
<module name="LeftCurly"/>
<module name="RightCurly">
<property name="option" value="alone"/>
</module>
<module name="NeedBraces"/>
<module name="AvoidNestedBlocks"/>
<!-- Class Design -->
<module name="FinalClass"/>
<module name="InterfaceIsType"/>
<module name="HideUtilityClassConstructor"/>
<module name="MutableException"/>
<module name="InnerTypeLast"/>
<module name="OneTopLevelClass"/>
<!-- Coding -->
<module name="CovariantEquals"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="InnerAssignment"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="NestedForDepth">
<property name="max" value="3"/>
</module>
<module name="NestedIfDepth">
<property name="max" value="3"/>
</module>
<module name="NestedTryDepth">
<property name="max" value="3"/>
</module>
<module name="MultipleVariableDeclarations"/>
<module name="RequireThis">
<property name="checkMethods" value="false"/>
</module>
<module name="OneStatementPerLine"/>
<!-- Imports -->
<module name="AvoidStarImport"/>
<module name="AvoidStaticImport">
<property name="excludes"
value="org.assertj.core.api.Assertions.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.AdditionalMatchers.*, org.mockito.ArgumentMatchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultHandlers.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo"/>
</module>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<module name="ImportOrder">
<property name="groups" value="java,/^javax?\./,*,org.springframework"/>
<property name="ordered" value="true"/>
<property name="separated" value="true"/>
<property name="option" value="bottom"/>
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<!-- Javadoc Comments -->
<module name="JavadocType">
<property name="scope" value="package"/>
<property name="authorFormat" value=".+\s.+"/>
</module>
<module name="JavadocMethod">
<property name="allowMissingJavadoc" value="true"/>
</module>
<module name="JavadocVariable">
<property name="scope" value="public"/>
</module>
<module name="JavadocStyle">
<property name="checkEmptyJavadoc" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation">
<property name="offset" value="0"/>
</module>
<module name="AtclauseOrder">
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
<property name="tagOrder" value="@param, @author, @since, @see, @version, @serial, @deprecated"/>
</module>
<module name="AtclauseOrder">
<property name="target" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
<property name="tagOrder" value="@param, @return, @throws, @since, @deprecated, @see"/>
</module>
<!-- Miscellaneous -->
<module name="CommentsIndentation"/>
<module name="UpperEll"/>
<module name="ArrayTypeStyle"/>
<module name="OuterTypeFilename"/>
<!-- Modifiers -->
<module name="RedundantModifier"/>
<!-- Regexp -->
<module name="RegexpSinglelineJava">
<property name="format" value="^\t* +\t*\S"/>
<property name="message" value="Line has leading space characters; indentation should be performed with tabs only."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="RegexpSinglelineJava">
<property name="maximum" value="0"/>
<property name="format" value="org\.mockito\.Mockito\.(when|doThrow|doAnswer)"/>
<property name="message"
value="Please use BDDMockto imports."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="RegexpSinglelineJava">
<property name="maximum" value="0"/>
<property name="format" value="org\.junit\.Assert\.assert"/>
<property name="message" value="Please use AssertJ imports."/>
<property name="ignoreComments" value="true"/>
</module>
<module name="Regexp">
<property name="format" value="[ \t]+$"/>
<property name="illegalPattern" value="true"/>
<property name="message" value="Trailing whitespace"/>
</module>
<!-- Whitespace -->
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter">
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS, ARRAY_DECLARATOR"/>
</module>
<module name="NoWhitespaceBefore"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
</module>
<module name="io.spring.javaformat.checkstyle.SpringChecks"/>
</module>

View File

@@ -2,17 +2,15 @@
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor"/>
<!-- global -->
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc*"/>
<suppress files="[\\/]src[\\/]test[\\/]java[\\/]" checks="Javadoc"/>
<suppress files="[\\/]src[\\/]integration-test[\\/]java[\\/]" checks="Javadoc"/>
<suppress files="[\\/]docs[\\/]" checks="Javadoc"/>
<suppress files="[\\/]docs[\\/]" checks="CommentsIndentation"/>
<!-- docs -->
<suppress files="[\\/]docs[\\/]" checks="Javadoc*"/>
<suppress files="[\\/]docs[\\/]" checks="AvoidStaticImport"/>
<suppress files="[\\/]docs[\\/]" checks="InnerTypeLast"/>
<suppress files="[\\/]samples[\\/]" checks="Javadoc"/>
<suppress files="[\\/]samples[\\/]" checks="CommentsIndentation"/>
<suppress files="[\\/]samples[\\/]" checks="InnerTypeLast"/>
<!-- samples -->
<suppress files="[\\/]samples[\\/]" checks="Javadoc*"/>
<suppress files="[\\/]samples[\\/].+Application\.java" checks="HideUtilityClassConstructor"/>
</suppressions>

View File

@@ -1,2 +1,2 @@
springBootVersion=2.0.2.RELEASE
version=2.0.4.RELEASE
springBootVersion=2.0.3.RELEASE
version=2.0.5.RELEASE

View File

@@ -2,10 +2,10 @@ dependencyManagement {
imports {
mavenBom 'com.fasterxml.jackson:jackson-bom:2.9.6'
mavenBom 'io.projectreactor:reactor-bom:Bismuth-SR10'
mavenBom 'org.springframework:spring-framework-bom:5.0.7.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR8'
mavenBom 'org.springframework.security:spring-security-bom:5.0.6.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.7.3'
mavenBom 'org.springframework:spring-framework-bom:5.0.8.RELEASE'
mavenBom 'org.springframework.data:spring-data-releasetrain:Kay-SR9'
mavenBom 'org.springframework.security:spring-security-bom:5.0.7.RELEASE'
mavenBom 'org.testcontainers:testcontainers-bom:1.8.1'
}
dependencies {
@@ -24,8 +24,8 @@ dependencyManagement {
dependency 'org.apache.derby:derby:10.14.2.0'
dependency 'org.assertj:assertj-core:3.10.0'
dependency 'org.hsqldb:hsqldb:2.4.1'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.2.5'
dependency 'org.mockito:mockito-core:2.18.3'
dependency 'org.postgresql:postgresql:42.2.2'
dependency 'org.mariadb.jdbc:mariadb-java-client:2.2.6'
dependency 'org.mockito:mockito-core:2.20.1'
dependency 'org.postgresql:postgresql:42.2.4'
}
}

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -46,7 +46,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class FindByUsernameTests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -92,7 +92,7 @@ public class SessionDetailsFilter extends OncePerRequestFilter {
}
return cityName + ", " + countryName;
}
catch (Exception e) {
catch (Exception ex) {
return UNKNOWN;
}

View File

@@ -50,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@AutoConfigureMockMvc
public class HttpRedisJsonTest {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class RedisSerializerTest {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@SpringSessionRedisOperations
private RedisTemplate<Object, Object> sessionRedisTemplate;

View File

@@ -45,7 +45,7 @@ import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDr
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
public class BootTests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private MockMvc mockMvc;

View File

@@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class AttributeTests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@LocalServerPort
private int port;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -59,7 +59,7 @@ public class UserRepositoryUserDetailsService implements UserDetailsService {
return new CustomUserDetails(user);
}
private final static class CustomUserDetails extends User implements UserDetails {
private static final class CustomUserDetails extends User implements UserDetails {
private CustomUserDetails(User user) {
super(user);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -42,7 +42,7 @@ public class WebSocketDisconnectHandler<S>
if (id == null) {
return;
}
this.repository.findById(id).ifPresent(user -> {
this.repository.findById(id).ifPresent((user) -> {
this.repository.deleteById(id);
this.messagingTemplate.convertAndSend("/topic/friends/signout",
Arrays.asList(user.getUsername()));

View File

@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Value("${local.server.port}")
private String port;

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -55,8 +55,8 @@ public class ObjectStreamSerializer implements StreamSerializer<Object> {
try {
return in.readObject();
}
catch (ClassNotFoundException e) {
throw new IOException(e);
catch (ClassNotFoundException ex) {
throw new IOException(ex);
}
}

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -54,7 +54,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@WebAppConfiguration
public class RestMockMvcTests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Autowired
private SessionRepositoryFilter<? extends Session> sessionRepositoryFilter;

View File

@@ -59,7 +59,7 @@ public class RestTests {
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> getForUser(this.baseUrl + "/", headers, String.class))
.isInstanceOf(HttpClientErrorException.class)
.satisfies(e -> assertThat(((HttpClientErrorException) e).getStatusCode())
.satisfies((e) -> assertThat(((HttpClientErrorException) e).getStatusCode())
.isEqualTo(HttpStatus.UNAUTHORIZED));
}

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -78,14 +78,14 @@ public class Initializer implements ServletContextListener {
socket = new ServerSocket(0);
return socket.getLocalPort();
}
catch (IOException e) {
throw new RuntimeException(e);
catch (IOException ex) {
throw new RuntimeException(ex);
}
finally {
try {
socket.close();
}
catch (IOException e) {
catch (IOException ex) {
}
}
}

View File

@@ -28,7 +28,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
@Profile("embedded-redis")
public class EmbeddedRedisConfig {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
@Bean
public GenericContainer redisContainer() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -20,6 +20,7 @@ import java.io.Serializable;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -177,7 +178,7 @@ public final class MapSession implements Session, Serializable {
@Override
public Set<String> getAttributeNames() {
return this.sessionAttrs.keySet();
return new HashSet<>(this.sessionAttrs.keySet());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.

View File

@@ -86,7 +86,7 @@ public class ReactiveMapSessionRepository implements ReactiveSessionRepository<M
public Mono<MapSession> findById(String id) {
// @formatter:off
return Mono.defer(() -> Mono.justOrEmpty(this.sessions.get(id))
.filter(session -> !session.isExpired())
.filter((session) -> !session.isExpired())
.map(MapSession::new)
.switchIfEmpty(deleteById(id).then(Mono.empty())));
// @formatter:on

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -47,7 +47,7 @@ public interface Session {
* Gets 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
* @param <T> the return type of the attribute
* @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
@@ -81,7 +81,7 @@ public interface Session {
@SuppressWarnings("unchecked")
default <T> T getAttributeOrDefault(String name, T defaultValue) {
T result = getAttribute(name);
return result == null ? defaultValue : result;
return (result != null ? result : defaultValue);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -110,8 +110,8 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
@PostConstruct
public void init() {
CookieSerializer cookieSerializer = this.cookieSerializer != null
? this.cookieSerializer : createDefaultCookieSerializer();
CookieSerializer cookieSerializer = (this.cookieSerializer != null
? this.cookieSerializer : createDefaultCookieSerializer());
this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
}
@@ -169,9 +169,9 @@ public class SpringHttpSessionConfiguration implements ApplicationContextAware {
try {
sessionCookieConfig = this.servletContext.getSessionCookieConfig();
}
catch (UnsupportedOperationException e) {
catch (UnsupportedOperationException ex) {
this.logger
.warn("Unable to obtain SessionCookieConfig: " + e.getMessage());
.warn("Unable to obtain SessionCookieConfig: " + ex.getMessage());
}
if (sessionCookieConfig != null) {
if (sessionCookieConfig.getName() != null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -45,7 +45,7 @@ public abstract class AbstractSessionEvent extends ApplicationEvent {
* implementations it may not be possible to get the original session in which case
* this may be null.
*
* @param <S> The type of Session
* @param <S> the type of Session
* @return the expired {@link Session} or null if the data store does not support
* obtaining it
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -63,7 +63,7 @@ class SpringSessionBackedSessionInformation<S extends Session>
/**
* Tries to determine the principal's name from the given Session.
*
* @param session Spring Session session
* @param session the session
* @return the principal's name, or empty String if it couldn't be determined
*/
private static String resolvePrincipal(Session session) {

View File

@@ -26,6 +26,9 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The default implementation of {@link CookieSerializer}.
*
@@ -36,6 +39,8 @@ import javax.servlet.http.HttpServletResponse;
*/
public class DefaultCookieSerializer implements CookieSerializer {
private static final Log logger = LogFactory.getLog(DefaultCookieSerializer.class);
private String cookieName = "SESSION";
private Boolean useSecureCookie;
@@ -69,8 +74,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
if (cookies != null) {
for (Cookie cookie : cookies) {
if (this.cookieName.equals(cookie.getName())) {
String sessionId = this.useBase64Encoding
? base64Decode(cookie.getValue()) : cookie.getValue();
String sessionId = (this.useBase64Encoding
? base64Decode(cookie.getValue()) : cookie.getValue());
if (sessionId == null) {
continue;
}
@@ -97,8 +102,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 + this.jvmRoute : requestedCookieValue);
Cookie sessionCookie = new Cookie(this.cookieName, this.useBase64Encoding
? base64Encode(actualCookieValue) : actualCookieValue);
@@ -140,7 +145,8 @@ public class DefaultCookieSerializer implements CookieSerializer {
byte[] decodedCookieBytes = Base64.getDecoder().decode(base64Value);
return new String(decodedCookieBytes);
}
catch (Exception e) {
catch (Exception ex) {
logger.debug("Unable to Base64 decode value: " + base64Value);
return null;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -98,8 +98,8 @@ public class HeaderHttpSessionIdResolver implements HttpSessionIdResolver {
@Override
public List<String> resolveSessionIds(HttpServletRequest request) {
String headerValue = request.getHeader(this.headerName);
return headerValue != null ? Collections.singletonList(headerValue)
: Collections.emptyList();
return (headerValue != null ? Collections.singletonList(headerValue)
: Collections.emptyList());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -174,11 +174,11 @@ abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
}
private void trackContentLength(byte[] content) {
checkContentLength(content == null ? 0 : content.length);
checkContentLength(content != null ? content.length : 0);
}
private void trackContentLength(char[] content) {
checkContentLength(content == null ? 0 : content.length);
checkContentLength(content != null ? content.length : 0);
}
private void trackContentLength(int content) {

View File

@@ -96,7 +96,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
@Override
public Mono<WebSession> retrieveSession(String sessionId) {
return this.sessions.findById(sessionId)
.doOnNext(session -> session.setLastAccessedTime(this.clock.instant()))
.doOnNext((session) -> session.setLastAccessedTime(this.clock.instant()))
.map(this::existingSession);
}
@@ -164,6 +164,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
@Override
public Mono<Void> invalidate() {
this.state.set(State.EXPIRED);
return SpringSessionWebSessionStore.this.sessions.deleteById(this.session.getId());
}
@@ -174,7 +175,14 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
@Override
public boolean isExpired() {
return this.session.isExpired();
if (this.state.get().equals(State.EXPIRED)) {
return true;
}
if (this.session.isExpired()) {
this.state.set(State.EXPIRED);
return true;
}
return false;
}
@Override
@@ -199,7 +207,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
}
private enum State {
NEW, STARTED
NEW, STARTED, EXPIRED
}
private static class SpringSessionMap implements Map<String, Object> {
@@ -231,7 +239,7 @@ public class SpringSessionWebSessionStore<S extends Session> implements WebSessi
@Override
public boolean containsValue(Object value) {
return this.session.getAttributeNames().stream()
.anyMatch(attrName -> this.session.getAttribute(attrName) != null);
.anyMatch((attrName) -> this.session.getAttribute(attrName) != null);
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -71,8 +71,9 @@ public final class WebSocketRegistryListener
SessionDisconnectEvent e = (SessionDisconnectEvent) event;
Map<String, Object> sessionAttributes = SimpMessageHeaderAccessor
.getSessionAttributes(e.getMessage().getHeaders());
String httpSessionId = sessionAttributes == null ? null
: SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes);
String httpSessionId = (sessionAttributes != null
? SessionRepositoryMessageInterceptor.getSessionId(sessionAttributes)
: null);
afterConnectionClosed(httpSessionId, e.getSessionId());
}
}
@@ -140,10 +141,10 @@ public final class WebSocketRegistryListener
try {
toClose.close(SESSION_EXPIRED_STATUS);
}
catch (IOException e) {
catch (IOException ex) {
logger.debug(
"Failed to close WebSocketSession (this is nothing to worry about but for debugging only)",
e);
ex);
}
}
}

View File

@@ -117,8 +117,8 @@ public final class SessionRepositoryMessageInterceptor<S extends Session>
}
Map<String, Object> sessionHeaders = SimpMessageHeaderAccessor
.getSessionAttributes(message.getHeaders());
String sessionId = sessionHeaders == null ? null
: (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME);
String sessionId = (sessionHeaders != null
? (String) sessionHeaders.get(SPRING_SESSION_ID_ATTR_NAME) : null);
if (sessionId != null) {
S session = this.sessionRepository.findById(sessionId);
if (session != null) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -100,4 +100,17 @@ public class MapSessionRepositoryTests {
assertThat(this.repository.findById(createSession.getId())).isNotNull();
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
MapSession session = this.repository.createSession();
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
}

View File

@@ -133,6 +133,18 @@ public class MapSessionTests {
assertThat(this.session.isExpired(now)).isTrue();
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
this.session.setAttribute("attribute1", "value1");
this.session.setAttribute("attribute2", "value2");
for (String attributeName : this.session.getAttributeNames()) {
this.session.removeAttribute(attributeName);
}
assertThat(this.session.getAttributeNames()).isEmpty();
}
static class CustomSession implements Session {
@Override

View File

@@ -144,4 +144,17 @@ public class ReactiveMapSessionRepositoryTests {
assertThat(this.repository.findById(createSession.getId()).block()).isNotNull();
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
MapSession session = this.repository.createSession().block();
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
}

View File

@@ -291,4 +291,21 @@ public class SpringSessionWebSessionStoreTests<S extends Session> {
.hasMessage("clock cannot be null");
}
@Test // gh-1114
public void createSessionThenSessionIsNotExpired() {
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
assertThat(createdWebSession.isExpired()).isFalse();
}
@Test // gh-1114
public void invalidateSessionThenSessionIsExpired() {
WebSession createdWebSession = this.webSessionStore.createWebSession().block();
given(createdWebSession.invalidate()).willReturn(Mono.empty());
createdWebSession.invalidate().block();
assertThat(createdWebSession.isExpired()).isTrue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -66,6 +66,6 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
}
private Object getLock(String sessionId) {
return this.locks.computeIfAbsent(sessionId, k -> new Object());
return this.locks.computeIfAbsent(sessionId, (k) -> new Object());
}
}

View File

@@ -29,7 +29,7 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
*/
public abstract class AbstractRedisITests {
private static final String DOCKER_IMAGE = "redis:4.0.9";
private static final String DOCKER_IMAGE = "redis:4.0.10";
protected static class BaseConfig {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -95,7 +95,7 @@ public class RedisListenerContainerTaskExecutorITests extends AbstractRedisITest
synchronized (this.lock) {
this.lock.wait(TimeUnit.SECONDS.toMillis(1));
}
return this.taskDispatched == null ? Boolean.FALSE : this.taskDispatched;
return (this.taskDispatched != null ? this.taskDispatched : Boolean.FALSE);
}
}

View File

@@ -134,7 +134,7 @@ public class ReactiveRedisOperationsSessionRepository implements
@Override
public Mono<Void> save(RedisSession session) {
return session.saveDelta().and(s -> {
return session.saveDelta().and((s) -> {
if (session.isNew) {
session.setNew(false);
}
@@ -148,9 +148,9 @@ public class ReactiveRedisOperationsSessionRepository implements
String sessionKey = getSessionKey(id);
return this.sessionRedisOperations.opsForHash().entries(sessionKey)
.collectMap(e -> e.getKey().toString(), Map.Entry::getValue)
.filter(map -> !map.isEmpty()).map(new SessionMapper(id))
.filter(session -> !session.isExpired()).map(RedisSession::new)
.collectMap((e) -> e.getKey().toString(), Map.Entry::getValue)
.filter((map) -> !map.isEmpty()).map(new SessionMapper(id))
.filter((session) -> !session.isExpired()).map(RedisSession::new)
.switchIfEmpty(Mono.defer(() -> deleteById(id).then(Mono.empty())));
}
@@ -311,7 +311,7 @@ public class ReactiveRedisOperationsSessionRepository implements
Mono<Boolean> setTtl = ReactiveRedisOperationsSessionRepository.this.sessionRedisOperations
.expire(sessionKey, getMaxInactiveInterval());
return changeSessionId.and(update).and(setTtl).and(s -> {
return changeSessionId.and(update).and(setTtl).and((s) -> {
this.delta.clear();
s.onComplete();
}).then();
@@ -322,7 +322,7 @@ public class ReactiveRedisOperationsSessionRepository implements
return Mono.empty();
}
Publisher<Void> replaceSessionId = s -> {
Publisher<Void> replaceSessionId = (s) -> {
this.originalSessionId = sessionId;
s.onComplete();
};

View File

@@ -316,7 +316,7 @@ public class RedisOperationsSessionRepository implements
/**
* Creates a new instance. For an example, refer to the class level javadoc.
*
* @param sessionRedisOperations The {@link RedisOperations} to use for managing the
* @param sessionRedisOperations the {@link RedisOperations} to use for managing the
* sessions. Cannot be null.
*/
public RedisOperationsSessionRepository(
@@ -797,8 +797,9 @@ public class RedisOperationsSessionRepository implements
this.delta = new HashMap<>(this.delta.size());
Long originalExpiration = this.originalLastAccessTime == null ? null
: this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli();
Long originalExpiration = (this.originalLastAccessTime != null
? this.originalLastAccessTime.plus(getMaxInactiveInterval()).toEpochMilli()
: null);
RedisOperationsSessionRepository.this.expirationPolicy
.onExpirationUpdated(originalExpiration, this);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -79,10 +79,10 @@ public class ConfigureNotifyKeyspaceEventsAction implements ConfigureRedisAction
}
return config.getProperty(config.stringPropertyNames().iterator().next());
}
catch (InvalidDataAccessApiUsageException e) {
catch (InvalidDataAccessApiUsageException ex) {
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);
ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -31,7 +31,7 @@ public interface ConfigureRedisAction {
/**
* A do nothing implementation of {@link ConfigureRedisAction}.
*/
ConfigureRedisAction NO_OP = connection -> {
ConfigureRedisAction NO_OP = (connection) -> {
};
}

View File

@@ -289,9 +289,9 @@ public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguratio
try {
connection.close();
}
catch (Exception e) {
catch (Exception ex) {
LogFactory.getLog(getClass()).error("Error closing RedisConnection",
e);
ex);
}
}
}

View File

@@ -133,7 +133,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
@Test
public void createSessionDefaultMaxInactiveInterval() {
StepVerifier.create(this.repository.createSession()).consumeNextWith(
session -> assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration
(session) -> assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration
.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS)))
.verifyComplete();
}
@@ -143,7 +143,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
this.repository.setDefaultMaxInactiveInterval(600);
StepVerifier.create(this.repository.createSession())
.consumeNextWith(session -> assertThat(session.getMaxInactiveInterval())
.consumeNextWith((session) -> assertThat(session.getMaxInactiveInterval())
.isEqualTo(Duration.ofSeconds(600)))
.verifyComplete();
}
@@ -157,7 +157,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
StepVerifier
.create(this.repository.createSession().doOnNext(this.repository::save))
.consumeNextWith(session -> {
.consumeNextWith((session) -> {
verify(this.redisOperations).opsForHash();
verify(this.hashOperations).putAll(anyString(), this.delta.capture());
verify(this.redisOperations).expire(anyString(), any());
@@ -325,7 +325,7 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
given(this.hashOperations.entries(anyString()))
.willReturn(Flux.fromIterable(map.entrySet()));
StepVerifier.create(this.repository.findById("test")).consumeNextWith(session -> {
StepVerifier.create(this.repository.findById("test")).consumeNextWith((session) -> {
verify(this.redisOperations).opsForHash();
verify(this.hashOperations).entries(anyString());
verifyZeroInteractions(this.redisOperations);
@@ -367,6 +367,19 @@ public class ReactiveRedisOperationsSessionRepositoryTests {
verifyZeroInteractions(this.hashOperations);
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
RedisSession session = this.repository.new RedisSession(this.cached);
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
private Map<String, Object> map(Object... objects) {
Map<String, Object> result = new HashMap<>();
if (objects == null) {

View File

@@ -868,6 +868,19 @@ public class RedisOperationsSessionRepositoryTests {
.hasMessage("namespace cannot be null or empty");
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
RedisSession session = this.redisRepository.new RedisSession(this.cached);
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
private String getKey(String id) {
return "spring:session:sessions:" + id;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -43,10 +43,10 @@ import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class RedisSessionExpirationPolicyTests {
// Wed Apr 15 10:28:32 CDT 2015
final static Long NOW = 1429111712346L;
static final Long NOW = 1429111712346L;
// Wed Apr 15 10:27:32 CDT 2015
final static Long ONE_MINUTE_AGO = 1429111652346L;
static final Long ONE_MINUTE_AGO = 1429111652346L;
@Mock
RedisOperations<Object, Object> sessionRedisOperations;

View File

@@ -10,4 +10,5 @@ dependencies {
testCompile "org.springframework.security:spring-security-core"
integrationTestCompile "com.hazelcast:hazelcast-client"
integrationTestCompile "org.testcontainers:testcontainers"
}

View File

@@ -18,9 +18,17 @@ package org.springframework.session.hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.instance.HazelcastInstanceProxy;
import org.junit.Assume;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.hazelcast.HazelcastSessionRepository.HazelcastSession;
@@ -34,8 +42,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public abstract class AbstractHazelcastRepositoryITests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
@Autowired
private HazelcastInstance hazelcast;
private HazelcastInstance hazelcastInstance;
@Autowired
private HazelcastSessionRepository repository;
@@ -45,7 +55,7 @@ public abstract class AbstractHazelcastRepositoryITests {
HazelcastSession sessionToSave = this.repository.createSession();
String sessionId = sessionToSave.getId();
IMap<String, MapSession> hazelcastMap = this.hazelcast
IMap<String, MapSession> hazelcastMap = this.hazelcastInstance
.getMap(HazelcastSessionRepository.DEFAULT_SESSION_MAP_NAME);
assertThat(hazelcastMap.size()).isEqualTo(0);
@@ -61,7 +71,7 @@ public abstract class AbstractHazelcastRepositoryITests {
}
@Test
public void changeSessionIdWhenOnlyChangeId() throws Exception {
public void changeSessionIdWhenOnlyChangeId() {
String attrName = "changeSessionId";
String attrValue = "changeSessionId-value";
HazelcastSession toSave = this.repository.createSession();
@@ -90,7 +100,7 @@ public abstract class AbstractHazelcastRepositoryITests {
}
@Test
public void changeSessionIdWhenChangeTwice() throws Exception {
public void changeSessionIdWhenChangeTwice() {
HazelcastSession toSave = this.repository.createSession();
this.repository.save(toSave);
@@ -109,7 +119,7 @@ public abstract class AbstractHazelcastRepositoryITests {
}
@Test
public void changeSessionIdWhenSetAttributeOnChangedSession() throws Exception {
public void changeSessionIdWhenSetAttributeOnChangedSession() {
String attrName = "changeSessionId";
String attrValue = "changeSessionId-value";
@@ -138,10 +148,7 @@ public abstract class AbstractHazelcastRepositoryITests {
}
@Test
public void changeSessionIdWhenHasNotSaved() throws Exception {
String attrName = "changeSessionId";
String attrValue = "changeSessionId-value";
public void changeSessionIdWhenHasNotSaved() {
HazelcastSession toSave = this.repository.createSession();
String originalId = toSave.getId();
toSave.changeSessionId();
@@ -167,4 +174,57 @@ public abstract class AbstractHazelcastRepositoryITests {
assertThat(this.repository.findById(sessionId)).isNull();
}
@Test
public void createAndUpdateSession() {
HazelcastSession session = this.repository.createSession();
String sessionId = session.getId();
this.repository.save(session);
session = this.repository.findById(sessionId);
session.setAttribute("attributeName", "attributeValue");
this.repository.save(session);
assertThat(this.repository.findById(sessionId)).isNotNull();
}
@Test
public void createSessionWithSecurityContextAndFindById() {
HazelcastSession session = this.repository.createSession();
String sessionId = session.getId();
Authentication authentication = new UsernamePasswordAuthenticationToken(
"saves-" + System.currentTimeMillis(), "password",
AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
session.setAttribute(SPRING_SECURITY_CONTEXT, securityContext);
this.repository.save(session);
assertThat(this.repository.findById(sessionId)).isNotNull();
}
@Test
public void createSessionWithSecurityContextAndFindByPrincipal() {
Assume.assumeTrue("Hazelcast runs in embedded server topology",
this.hazelcastInstance instanceof HazelcastInstanceProxy);
HazelcastSession session = this.repository.createSession();
String username = "saves-" + System.currentTimeMillis();
Authentication authentication = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
session.setAttribute(SPRING_SECURITY_CONTEXT, securityContext);
this.repository.save(session);
assertThat(this.repository.findByIndexNameAndIndexValue(
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username))
.isNotNull();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -22,14 +22,17 @@ import com.hazelcast.core.HazelcastInstance;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.SocketUtils;
/**
* Integration tests that check the underlying data source - in this case Hazelcast
@@ -44,20 +47,23 @@ import org.springframework.util.SocketUtils;
@WebAppConfiguration
public class HazelcastClientRepositoryITests extends AbstractHazelcastRepositoryITests {
private static final int PORT = SocketUtils.findAvailableTcpPort();
private static HazelcastInstance hazelcastInstance;
private static GenericContainer container = new GenericContainer<>(
"hazelcast/hazelcast:3.9.4")
.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);
@BeforeClass
public static void setup() {
hazelcastInstance = HazelcastITestUtils.embeddedHazelcastServer(PORT);
public static void setUpClass() {
container.start();
}
@AfterClass
public static void teardown() {
if (hazelcastInstance != null) {
hazelcastInstance.shutdown();
}
public static void tearDownClass() {
container.stop();
}
@Configuration
@@ -65,9 +71,13 @@ public class HazelcastClientRepositoryITests extends AbstractHazelcastRepository
static class HazelcastSessionConfig {
@Bean
public HazelcastInstance embeddedHazelcastClient() {
public HazelcastInstance hazelcastInstance() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.getNetworkConfig().addAddress("127.0.0.1:" + PORT);
clientConfig.getNetworkConfig().addAddress(container.getContainerIpAddress()
+ ":" + container.getFirstMappedPort());
clientConfig.getUserCodeDeploymentConfig().setEnabled(true)
.addClass(Session.class).addClass(MapSession.class)
.addClass(SessionUpdateEntryProcessor.class);
return HazelcastClient.newHazelcastClient(clientConfig);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -43,7 +43,7 @@ public class HazelcastServerRepositoryITests extends AbstractHazelcastRepository
static class HazelcastSessionConfig {
@Bean
public HazelcastInstance embeddedHazelcastServer() {
public HazelcastInstance hazelcastInstance() {
return HazelcastITestUtils.embeddedHazelcastServer();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -66,6 +66,6 @@ public class SessionEventRegistry implements ApplicationListener<AbstractSession
}
private Object getLock(String sessionId) {
return this.locks.computeIfAbsent(sessionId, k -> new Object());
return this.locks.computeIfAbsent(sessionId, (k) -> new Object());
}
}

View File

@@ -59,7 +59,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@WebAppConfiguration
public class EnableHazelcastHttpSessionEventsTests<S extends Session> {
private final static int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1;
@Autowired
private SessionRepository<S> repository;

View File

@@ -0,0 +1,11 @@
<?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 hazelcast-config-3.9.xsd">
<user-code-deployment enabled="true">
<class-cache-mode>ETERNAL</class-cache-mode>
<provider-mode>LOCAL_AND_CACHED_CLASSES</provider-mode>
</user-code-deployment>
</hazelcast>

View File

@@ -31,8 +31,6 @@ import javax.annotation.PreDestroy;
import com.hazelcast.core.EntryEvent;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IMap;
import com.hazelcast.map.AbstractEntryProcessor;
import com.hazelcast.map.EntryProcessor;
import com.hazelcast.map.listener.EntryAddedListener;
import com.hazelcast.map.listener.EntryEvictedListener;
import com.hazelcast.map.listener.EntryRemovedListener;
@@ -452,59 +450,4 @@ public class HazelcastSessionRepository implements
}
/**
* Hazelcast {@link EntryProcessor} responsible for handling updates to session.
*
* @since 2.0.0
* @see #save(HazelcastSession)
*/
private static final class SessionUpdateEntryProcessor
extends AbstractEntryProcessor<String, MapSession> {
private Instant lastAccessedTime;
private Duration maxInactiveInterval;
private Map<String, Object> delta;
@Override
public Object process(Map.Entry<String, MapSession> entry) {
MapSession value = entry.getValue();
if (value == null) {
return Boolean.FALSE;
}
if (this.lastAccessedTime != null) {
value.setLastAccessedTime(this.lastAccessedTime);
}
if (this.maxInactiveInterval != null) {
value.setMaxInactiveInterval(this.maxInactiveInterval);
}
if (this.delta != null) {
for (final Map.Entry<String, Object> attribute : this.delta.entrySet()) {
if (attribute.getValue() != null) {
value.setAttribute(attribute.getKey(), attribute.getValue());
}
else {
value.removeAttribute(attribute.getKey());
}
}
}
entry.setValue(value);
return Boolean.TRUE;
}
public void setLastAccessedTime(Instant lastAccessedTime) {
this.lastAccessedTime = lastAccessedTime;
}
public void setMaxInactiveInterval(Duration maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
public void setDelta(Map<String, Object> delta) {
this.delta = delta;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2014-2018 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.hazelcast;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import com.hazelcast.map.AbstractEntryProcessor;
import com.hazelcast.map.EntryProcessor;
import org.springframework.session.MapSession;
/**
* Hazelcast {@link EntryProcessor} responsible for handling updates to session.
*
* @author Vedran Pavic
* @since 2.0.5
* @see HazelcastSessionRepository#save(HazelcastSessionRepository.HazelcastSession)
*/
class SessionUpdateEntryProcessor extends AbstractEntryProcessor<String, MapSession> {
private Instant lastAccessedTime;
private Duration maxInactiveInterval;
private Map<String, Object> delta;
@Override
public Object process(Map.Entry<String, MapSession> entry) {
MapSession value = entry.getValue();
if (value == null) {
return Boolean.FALSE;
}
if (this.lastAccessedTime != null) {
value.setLastAccessedTime(this.lastAccessedTime);
}
if (this.maxInactiveInterval != null) {
value.setMaxInactiveInterval(this.maxInactiveInterval);
}
if (this.delta != null) {
for (final Map.Entry<String, Object> attribute : this.delta.entrySet()) {
if (attribute.getValue() != null) {
value.setAttribute(attribute.getKey(), attribute.getValue());
}
else {
value.removeAttribute(attribute.getKey());
}
}
}
entry.setValue(value);
return Boolean.TRUE;
}
void setLastAccessedTime(Instant lastAccessedTime) {
this.lastAccessedTime = lastAccessedTime;
}
void setMaxInactiveInterval(Duration maxInactiveInterval) {
this.maxInactiveInterval = maxInactiveInterval;
}
void setDelta(Map<String, Object> delta) {
this.delta = delta;
}
}

View File

@@ -418,4 +418,17 @@ public class HazelcastSessionRepositoryTests {
verifyZeroInteractions(this.sessions);
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
HazelcastSession session = this.repository.createSession();
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
}

View File

@@ -237,7 +237,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -263,7 +263,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -309,7 +309,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -360,7 +360,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -423,7 +423,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -448,7 +448,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -491,7 +491,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});
@@ -539,7 +539,7 @@ public abstract class AbstractJdbcOperationsSessionRepositoryITests {
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
findByPrincipalName.values().forEach(session -> {
findByPrincipalName.values().forEach((session) -> {
assertThat(session.isChanged()).isFalse();
assertThat(session.getDelta()).isEmpty();
});

View File

@@ -86,7 +86,7 @@ public class MariaDb10JdbcOperationsSessionRepositoryITests
private static class MariaDb10Container extends MariaDBContainer<MariaDb10Container> {
MariaDb10Container() {
super("mariadb:10.3.7");
super("mariadb:10.3.8");
}
@Override

View File

@@ -21,7 +21,6 @@ import javax.sql.DataSource;
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.testcontainers.containers.MSSQLServerContainer;
@@ -40,7 +39,6 @@ import org.springframework.test.context.web.WebAppConfiguration;
*
* @author Vedran Pavic
*/
@Ignore
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration
@@ -88,12 +86,9 @@ public class SqlServerJdbcOperationsSessionRepositoryITests
extends MSSQLServerContainer<SqlServer2007Container> {
SqlServer2007Container() {
super("microsoft/mssql-server-linux:2017-CU7");
}
@Override
protected int getStartupTimeoutSeconds() {
return 240;
super("microsoft/mssql-server-linux:2017-CU8");
withStartupTimeoutSeconds(240);
withConnectTimeoutSeconds(240);
}
}

View File

@@ -0,0 +1 @@
microsoft/mssql-server-linux:2017-CU8

View File

@@ -372,7 +372,7 @@ public class JdbcOperationsSessionRepository implements
protected void doInTransactionWithoutResult(TransactionStatus status) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
JdbcOperationsSessionRepository.this.createSessionQuery,
ps -> {
(ps) -> {
ps.setString(1, session.primaryKey);
ps.setString(2, session.getId());
ps.setLong(3, session.getCreationTime().toEpochMilli());
@@ -397,7 +397,7 @@ public class JdbcOperationsSessionRepository implements
if (session.isChanged()) {
JdbcOperationsSessionRepository.this.jdbcOperations.update(
JdbcOperationsSessionRepository.this.updateSessionQuery,
ps -> {
(ps) -> {
ps.setString(1, session.getId());
ps.setLong(2, session.getLastAccessedTime().toEpochMilli());
ps.setInt(3, (int) session.getMaxInactiveInterval().getSeconds());
@@ -407,17 +407,17 @@ public class JdbcOperationsSessionRepository implements
});
}
List<String> addedAttributeNames = session.delta.entrySet().stream()
.filter(entry -> entry.getValue() == DeltaValue.ADDED)
.filter((entry) -> entry.getValue() == DeltaValue.ADDED)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
insertSessionAttributes(session, addedAttributeNames);
List<String> updatedAttributeNames = session.delta.entrySet().stream()
.filter(entry -> entry.getValue() == DeltaValue.UPDATED)
.filter((entry) -> entry.getValue() == DeltaValue.UPDATED)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
updateSessionAttributes(session, updatedAttributeNames);
List<String> removedAttributeNames = session.delta.entrySet().stream()
.filter(entry -> entry.getValue() == DeltaValue.REMOVED)
.filter((entry) -> entry.getValue() == DeltaValue.REMOVED)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
deleteSessionAttributes(session, removedAttributeNames);
@@ -430,10 +430,10 @@ public class JdbcOperationsSessionRepository implements
@Override
public JdbcSession findById(final String id) {
final JdbcSession session = this.transactionOperations.execute(status -> {
final JdbcSession session = this.transactionOperations.execute((status) -> {
List<JdbcSession> sessions = JdbcOperationsSessionRepository.this.jdbcOperations.query(
JdbcOperationsSessionRepository.this.getSessionQuery,
ps -> ps.setString(1, id),
(ps) -> ps.setString(1, id),
JdbcOperationsSessionRepository.this.extractor
);
if (sessions.isEmpty()) {
@@ -473,10 +473,10 @@ public class JdbcOperationsSessionRepository implements
return Collections.emptyMap();
}
List<JdbcSession> sessions = this.transactionOperations.execute(status ->
List<JdbcSession> sessions = this.transactionOperations.execute((status) ->
JdbcOperationsSessionRepository.this.jdbcOperations.query(
JdbcOperationsSessionRepository.this.listSessionsByPrincipalNameQuery,
ps -> ps.setString(1, indexValue),
(ps) -> ps.setString(1, indexValue),
JdbcOperationsSessionRepository.this.extractor));
Map<String, JdbcSession> sessionMap = new HashMap<>(
@@ -512,7 +512,7 @@ public class JdbcOperationsSessionRepository implements
});
}
else {
this.jdbcOperations.update(this.createSessionAttributeQuery, ps -> {
this.jdbcOperations.update(this.createSessionAttributeQuery, (ps) -> {
String attributeName = attributeNames.get(0);
ps.setString(1, session.primaryKey);
ps.setString(2, attributeName);
@@ -544,7 +544,7 @@ public class JdbcOperationsSessionRepository implements
});
}
else {
this.jdbcOperations.update(this.updateSessionAttributeQuery, ps -> {
this.jdbcOperations.update(this.updateSessionAttributeQuery, (ps) -> {
String attributeName = attributeNames.get(0);
serialize(ps, 1, session.getAttribute(attributeName));
ps.setString(2, session.primaryKey);
@@ -575,7 +575,7 @@ public class JdbcOperationsSessionRepository implements
});
}
else {
this.jdbcOperations.update(this.deleteSessionAttributeQuery, ps -> {
this.jdbcOperations.update(this.deleteSessionAttributeQuery, (ps) -> {
String attributeName = attributeNames.get(0);
ps.setString(1, session.primaryKey);
ps.setString(2, attributeName);
@@ -584,7 +584,7 @@ public class JdbcOperationsSessionRepository implements
}
public void cleanUpExpiredSessions() {
Integer deletedCount = this.transactionOperations.execute(transactionStatus ->
Integer deletedCount = this.transactionOperations.execute((status) ->
JdbcOperationsSessionRepository.this.jdbcOperations.update(
JdbcOperationsSessionRepository.this.deleteSessionsByExpiryTimeQuery,
System.currentTimeMillis()));
@@ -740,22 +740,22 @@ public class JdbcOperationsSessionRepository implements
if (attributeExists) {
if (attributeRemoved) {
this.delta.merge(attributeName, DeltaValue.REMOVED,
(oldDeltaValue, deltaValue) -> oldDeltaValue == DeltaValue.ADDED
(oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED
? null
: deltaValue);
: deltaValue));
}
else {
this.delta.merge(attributeName, DeltaValue.UPDATED,
(oldDeltaValue, deltaValue) -> oldDeltaValue == DeltaValue.ADDED
(oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED
? oldDeltaValue
: deltaValue);
: deltaValue));
}
}
else {
this.delta.merge(attributeName, DeltaValue.ADDED,
(oldDeltaValue, deltaValue) -> oldDeltaValue == DeltaValue.ADDED
(oldDeltaValue, deltaValue) -> (oldDeltaValue == DeltaValue.ADDED
? oldDeltaValue
: DeltaValue.UPDATED);
: DeltaValue.UPDATED));
}
this.delegate.setAttribute(attributeName, attributeValue);
if (PRINCIPAL_NAME_INDEX_NAME.equals(attributeName) ||

View File

@@ -675,6 +675,19 @@ public class JdbcOperationsSessionRepositoryTests {
verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), anyLong());
}
@Test // gh-1120
public void getAttributeNamesAndRemove() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository.createSession();
session.setAttribute("attribute1", "value1");
session.setAttribute("attribute2", "value2");
for (String attributeName : session.getAttributeNames()) {
session.removeAttribute(attributeName);
}
assertThat(session.getAttributeNames()).isEmpty();
}
private void assertPropagationRequiresNew() {
ArgumentCaptor<TransactionDefinition> argument =
ArgumentCaptor.forClass(TransactionDefinition.class);