From 910b7826ddbabaa4c2a36a657d10b35a049e0ec5 Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Wed, 5 Aug 2020 13:16:17 +0300 Subject: [PATCH 1/7] Add serializer. --- .../hazelcast/HazelcastSessionSerializer.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java new file mode 100644 index 000000000..ec785f0c4 --- /dev/null +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java @@ -0,0 +1,87 @@ +package org.springframework.session.hazelcast; + +import com.hazelcast.nio.ObjectDataInput; +import com.hazelcast.nio.ObjectDataOutput; +import com.hazelcast.nio.serialization.StreamSerializer; +import org.springframework.session.MapSession; + +import java.io.EOFException; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; + +public class HazelcastSessionSerializer implements StreamSerializer { + + private static final int SERIALIZER_TYPE_ID = 12345; + + @Override + public void write(ObjectDataOutput out, MapSession session) throws IOException { + out.writeUTF(session.getOriginalId()); + out.writeUTF(session.getId()); + writeInstant(out, session.getCreationTime()); + writeInstant(out, session.getLastAccessedTime()); + writeDuration(out, session.getMaxInactiveInterval()); + for (String attrName : session.getAttributeNames()) { + Object attrValue = session.getAttribute(attrName); + if (attrValue != null) { + out.writeUTF(attrName); + out.writeObject(attrValue); + } + } + } + + private void writeInstant(ObjectDataOutput out, Instant instant) throws IOException { + out.writeLong(instant.getEpochSecond()); + out.writeInt(instant.getNano()); + } + + private void writeDuration(ObjectDataOutput out, Duration duration) throws IOException { + out.writeLong(duration.getSeconds()); + out.writeInt(duration.getNano()); + } + + @Override + public MapSession read(ObjectDataInput in) throws IOException { + String originalId = in.readUTF(); + MapSession cached = new MapSession(originalId); + cached.setId(in.readUTF()); + cached.setCreationTime(readInstant(in)); + cached.setLastAccessedTime(readInstant(in)); + cached.setMaxInactiveInterval(readDuration(in)); + try { + while (true) { + // During write, it's not possible to write + // number of non-null attributes without an extra + // iteration. Hence the attributes are read until + // EOF here. + String attrName = in.readUTF(); + Object attrValue = in.readObject(); + cached.setAttribute(attrName, attrValue); + } + } catch (EOFException ignored) { + } + return cached; + } + + private Instant readInstant(ObjectDataInput in) throws IOException { + long seconds = in.readLong(); + int nanos = in.readInt(); + return Instant.ofEpochSecond(seconds, nanos); + } + + private Duration readDuration(ObjectDataInput in) throws IOException { + long seconds = in.readLong(); + int nanos = in.readInt(); + return Duration.ofSeconds(seconds, nanos); + } + + @Override + public int getTypeId() { + return SERIALIZER_TYPE_ID; + } + + @Override + public void destroy() { + } + +} From 461933854dd242753458324c15db5126e6f2554b Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Fri, 7 Aug 2020 12:16:07 +0300 Subject: [PATCH 2/7] Add javadoc, use new serializer for tests. --- .../hazelcast/HazelcastITestUtils.java | 5 ++ .../hazelcast/HazelcastSessionSerializer.java | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java index 00eec67b2..608de19c2 100644 --- a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java +++ b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java @@ -20,8 +20,10 @@ import com.hazelcast.config.MapAttributeConfig; import com.hazelcast.config.MapIndexConfig; import com.hazelcast.config.NetworkConfig; +import com.hazelcast.config.SerializerConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; +import org.springframework.session.MapSession; /** * Utility class for Hazelcast integration tests. @@ -48,6 +50,9 @@ static HazelcastInstance embeddedHazelcastServer() { config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) .addMapAttributeConfig(attributeConfig).addMapIndexConfig( new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false)); + SerializerConfig serializerConfig = new SerializerConfig(); + serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class); + config.getSerializationConfig().addSerializerConfig(serializerConfig); return Hazelcast.newHazelcastInstance(config); } diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java index ec785f0c4..5ad6ab63b 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java @@ -10,6 +10,56 @@ import java.time.Duration; import java.time.Instant; +/** + * A {@link com.hazelcast.nio.serialization.Serializer} implementation that + * handles the (de)serialization of {@link MapSession} stored on {@link com.hazelcast.core.IMap}. + * + * The use of this serializer is optional and provides faster serialization of + * sessions. If not configured to be used, Hazelcast will serialize sessions + * via {@link java.io.Serializable} by default. + * + * If multiple instances of a Spring application is run, then all of them need to use + * the same serialization method. If this serializer is registered on one instance + * and not another one, then it will end up with HazelcastSerializationException. + * The same applies when clients are configured to use this serializer but not the + * members, and vice versa. Also note that, if a new instance is created with this + * serialization but the existing Hazelcast cluster contains the values not serialized + * by this but instead the default one, this will result in incompatibility again. + * + *

+ * An example of how to register the serializer on embedded instance can be seen below: + * + *

+ * Config config = new Config();
+ *
+ * // ... other configurations for Hazelcast ...
+ *
+ * SerializerConfig serializerConfig = new SerializerConfig();
+ * serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
+ * config.getSerializationConfig().addSerializerConfig(serializerConfig);
+ *
+ * HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);
+ * 
+ * + * Below is the example of how to register the serializer on client instance. Note that, + * to use the serializer in client/server mode, the serializer - and hence {@link MapSession}, + * must exist on the server's classpath and must be registered via {@link com.hazelcast.config.SerializerConfig} + * with the configuration above for each server. + * + *
+ * ClientConfig clientConfig = new ClientConfig();
+ *
+ * // ... other configurations for Hazelcast Client ...
+ *
+ * SerializerConfig serializerConfig = new SerializerConfig();
+ * serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class);
+ * clientConfig.getSerializationConfig().addSerializerConfig(serializerConfig);
+ *
+ * HazelcastInstance hazelcastClient = HazelcastClient.newHazelcastClient(clientConfig);
+ * 
+ * + * @author Enes Ozcan + */ public class HazelcastSessionSerializer implements StreamSerializer { private static final int SERIALIZER_TYPE_ID = 12345; From 5e7ca6dd5bdf1713fd2b2f9e6bae0aafa8e4bc73 Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Fri, 7 Aug 2020 14:42:56 +0300 Subject: [PATCH 3/7] Add serializer config to docs. --- .../src/docs/asciidoc/guides/java-hazelcast.adoc | 9 ++++++++- .../test/java/docs/http/HazelcastHttpSessionConfig.java | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/spring-session-docs/src/docs/asciidoc/guides/java-hazelcast.adoc b/spring-session-docs/src/docs/asciidoc/guides/java-hazelcast.adoc index 03727f497..b0fd7d201 100644 --- a/spring-session-docs/src/docs/asciidoc/guides/java-hazelcast.adoc +++ b/spring-session-docs/src/docs/asciidoc/guides/java-hazelcast.adoc @@ -97,11 +97,18 @@ The filter is in charge of replacing the `HttpSession` implementation to be back In this instance, Spring Session is backed by Hazelcast. <2> In order to support retrieval of sessions by principal name index, an appropriate `ValueExtractor` needs to be registered. Spring Session provides `PrincipalNameExtractor` for this purpose. -<3> We create a `HazelcastInstance` that connects Spring Session to Hazelcast. +<3> In order to serialize `MapSession` objects efficiently, `HazelcastSessionSerializer` needs to be registered. If this +is not set, Hazelcast will serialize sessions using native Java serialization. +<4> We create a `HazelcastInstance` that connects Spring Session to Hazelcast. By default, the application starts and connects to an embedded instance of Hazelcast. For more information on configuring Hazelcast, see the https://docs.hazelcast.org/docs/{hazelcast-version}/manual/html-single/index.html#hazelcast-configuration[reference documentation]. ==== +NOTE: If `HazelcastSessionSerializer` is preferred, it needs to be configured for all Hazelcast cluster members before they start. +In a Hazelcast cluster, all members should use the same serialization method for sessions. Also, if Hazelcast Client/Server topology +is used, then both members and clients must use the same serialization method. The serializer can be registered via `ClientConfig` +with the same `SerializerConfiguration` of members. + == Servlet Container Initialization Our <> created a Spring bean named `springSessionRepositoryFilter` that implements `Filter`. diff --git a/spring-session-docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java b/spring-session-docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java index 3aad03149..c77bc1d32 100644 --- a/spring-session-docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java +++ b/spring-session-docs/src/test/java/docs/http/HazelcastHttpSessionConfig.java @@ -19,12 +19,15 @@ import com.hazelcast.config.Config; import com.hazelcast.config.MapAttributeConfig; import com.hazelcast.config.MapIndexConfig; +import com.hazelcast.config.SerializerConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.session.MapSession; import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository; +import org.springframework.session.hazelcast.HazelcastSessionSerializer; import org.springframework.session.hazelcast.PrincipalNameExtractor; import org.springframework.session.hazelcast.config.annotation.web.http.EnableHazelcastHttpSession; @@ -42,7 +45,10 @@ public HazelcastInstance hazelcastInstance() { config.getMapConfig(HazelcastIndexedSessionRepository.DEFAULT_SESSION_MAP_NAME) // <2> .addMapAttributeConfig(attributeConfig).addMapIndexConfig( new MapIndexConfig(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false)); - return Hazelcast.newHazelcastInstance(config); // <3> + SerializerConfig serializerConfig = new SerializerConfig(); + serializerConfig.setImplementation(new HazelcastSessionSerializer()).setTypeClass(MapSession.class); + config.getSerializationConfig().addSerializerConfig(serializerConfig); // <3> + return Hazelcast.newHazelcastInstance(config); // <4> } } From c5a0bd26020dae8d039c37aa28427e6c0bbc992e Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Fri, 7 Aug 2020 14:59:55 +0300 Subject: [PATCH 4/7] Use random id, fix javadoc. --- .../session/hazelcast/HazelcastSessionSerializer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java index 5ad6ab63b..65d9ec402 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java @@ -14,10 +14,12 @@ * A {@link com.hazelcast.nio.serialization.Serializer} implementation that * handles the (de)serialization of {@link MapSession} stored on {@link com.hazelcast.core.IMap}. * + *

* The use of this serializer is optional and provides faster serialization of * sessions. If not configured to be used, Hazelcast will serialize sessions * via {@link java.io.Serializable} by default. * + *

* If multiple instances of a Spring application is run, then all of them need to use * the same serialization method. If this serializer is registered on one instance * and not another one, then it will end up with HazelcastSerializationException. @@ -62,7 +64,7 @@ */ public class HazelcastSessionSerializer implements StreamSerializer { - private static final int SERIALIZER_TYPE_ID = 12345; + private static final int SERIALIZER_TYPE_ID = 1453; @Override public void write(ObjectDataOutput out, MapSession session) throws IOException { From e2f5d0856a3aa77fd7bee360d9f622e8577fd227 Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Fri, 7 Aug 2020 15:18:52 +0300 Subject: [PATCH 5/7] Add test. --- .../HazelcastSessionSerializerTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java diff --git a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java new file mode 100644 index 000000000..17e749f57 --- /dev/null +++ b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java @@ -0,0 +1,81 @@ +package org.springframework.session.hazelcast; + +import com.hazelcast.config.SerializationConfig; +import com.hazelcast.config.SerializerConfig; +import com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder; +import com.hazelcast.nio.serialization.Data; +import com.hazelcast.spi.serialization.SerializationService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.session.MapSession; + +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; + +class HazelcastSessionSerializerTests { + + private SerializationService serializationService; + + @BeforeEach + void setUp() { + SerializationConfig serializationConfig = new SerializationConfig(); + SerializerConfig serializerConfig = new SerializerConfig() + .setImplementation(new HazelcastSessionSerializer()) + .setTypeClass(MapSession.class); + serializationConfig.addSerializerConfig(serializerConfig); + serializationService = new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build(); + } + + @Test + void serializeSessionWithStreamSerializer() { + MapSession originalSession = new MapSession(); + originalSession.setAttribute("attr1", "value1"); + originalSession.setAttribute("attr2", "value2"); + originalSession.setAttribute("attr3", new SerializableTestAttribute(3)); + originalSession.setMaxInactiveInterval(Duration.ofDays(5)); + originalSession.setLastAccessedTime(Instant.now()); + originalSession.setId("custom-id"); + + Data serialized = serializationService.toData(originalSession); + MapSession cached = serializationService.toObject(serialized); + + assertThat(originalSession.getCreationTime()).isEqualTo(cached.getCreationTime()); + assertThat(originalSession.getMaxInactiveInterval()).isEqualTo(cached.getMaxInactiveInterval()); + assertThat(originalSession.getId()).isEqualTo(cached.getId()); + assertThat(originalSession.getOriginalId()).isEqualTo(cached.getOriginalId()); + assertThat(originalSession.getAttributeNames().size()).isEqualTo(cached.getAttributeNames().size()); + assertThat(originalSession.getAttribute("attr1")) + .isEqualTo(cached.getAttribute("attr1")); + assertThat(originalSession.getAttribute("attr2")) + .isEqualTo(cached.getAttribute("attr2")); + assertThat(originalSession.getAttribute("attr3")) + .isEqualTo(cached.getAttribute("attr3")); + } + + + static class SerializableTestAttribute implements Serializable { + + private int id; + + public SerializableTestAttribute(int id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SerializableTestAttribute)) return false; + SerializableTestAttribute that = (SerializableTestAttribute) o; + return id == that.id; + } + + @Override + public int hashCode() { + return id; + } + } + +} From c8d73a3b0eb53ce04db66518327545a734a09d58 Mon Sep 17 00:00:00 2001 From: Enes Ozcan Date: Fri, 7 Aug 2020 16:02:50 +0300 Subject: [PATCH 6/7] Fix checkstyle. --- .../hazelcast/HazelcastITestUtils.java | 1 + .../hazelcast/HazelcastSessionSerializer.java | 61 ++++++++++++------- ...a => HazelcastSessionSerializerTests.java} | 56 +++++++++++------ 3 files changed, 78 insertions(+), 40 deletions(-) rename spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/{HazelcastSessionSerializerTest.java => HazelcastSessionSerializerTests.java} (60%) diff --git a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java index 608de19c2..76158a0d5 100644 --- a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java +++ b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/HazelcastITestUtils.java @@ -23,6 +23,7 @@ import com.hazelcast.config.SerializerConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; + import org.springframework.session.MapSession; /** diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java index 65d9ec402..27c94cef7 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java @@ -1,32 +1,49 @@ -package org.springframework.session.hazelcast; +/* + * Copyright 2014-2020 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. + */ -import com.hazelcast.nio.ObjectDataInput; -import com.hazelcast.nio.ObjectDataOutput; -import com.hazelcast.nio.serialization.StreamSerializer; -import org.springframework.session.MapSession; +package org.springframework.session.hazelcast; import java.io.EOFException; import java.io.IOException; import java.time.Duration; import java.time.Instant; +import com.hazelcast.nio.ObjectDataInput; +import com.hazelcast.nio.ObjectDataOutput; +import com.hazelcast.nio.serialization.StreamSerializer; + +import org.springframework.session.MapSession; + /** - * A {@link com.hazelcast.nio.serialization.Serializer} implementation that - * handles the (de)serialization of {@link MapSession} stored on {@link com.hazelcast.core.IMap}. + * A {@link com.hazelcast.nio.serialization.Serializer} implementation that handles the + * (de)serialization of {@link MapSession} stored on {@link com.hazelcast.core.IMap}. * *

- * The use of this serializer is optional and provides faster serialization of - * sessions. If not configured to be used, Hazelcast will serialize sessions - * via {@link java.io.Serializable} by default. + * The use of this serializer is optional and provides faster serialization of sessions. + * If not configured to be used, Hazelcast will serialize sessions via + * {@link java.io.Serializable} by default. * *

- * If multiple instances of a Spring application is run, then all of them need to use - * the same serialization method. If this serializer is registered on one instance - * and not another one, then it will end up with HazelcastSerializationException. - * The same applies when clients are configured to use this serializer but not the - * members, and vice versa. Also note that, if a new instance is created with this - * serialization but the existing Hazelcast cluster contains the values not serialized - * by this but instead the default one, this will result in incompatibility again. + * If multiple instances of a Spring application is run, then all of them need to use the + * same serialization method. If this serializer is registered on one instance and not + * another one, then it will end up with HazelcastSerializationException. The same applies + * when clients are configured to use this serializer but not the members, and vice versa. + * Also note that, if a new instance is created with this serialization but the existing + * Hazelcast cluster contains the values not serialized by this but instead the default + * one, this will result in incompatibility again. * *

* An example of how to register the serializer on embedded instance can be seen below: @@ -44,9 +61,10 @@ * * * Below is the example of how to register the serializer on client instance. Note that, - * to use the serializer in client/server mode, the serializer - and hence {@link MapSession}, - * must exist on the server's classpath and must be registered via {@link com.hazelcast.config.SerializerConfig} - * with the configuration above for each server. + * to use the serializer in client/server mode, the serializer - and hence + * {@link MapSession}, must exist on the server's classpath and must be registered via + * {@link com.hazelcast.config.SerializerConfig} with the configuration above for each + * server. * *

  * ClientConfig clientConfig = new ClientConfig();
@@ -110,7 +128,8 @@ public MapSession read(ObjectDataInput in) throws IOException {
 				Object attrValue = in.readObject();
 				cached.setAttribute(attrName, attrValue);
 			}
-		} catch (EOFException ignored) {
+		}
+		catch (EOFException ignored) {
 		}
 		return cached;
 	}
diff --git a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTests.java
similarity index 60%
rename from spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java
rename to spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTests.java
index 17e749f57..b79a3bf1e 100644
--- a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTest.java
+++ b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastSessionSerializerTests.java
@@ -1,5 +1,25 @@
+/*
+ * Copyright 2014-2020 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;
 
+import java.io.Serializable;
+import java.time.Duration;
+import java.time.Instant;
+
 import com.hazelcast.config.SerializationConfig;
 import com.hazelcast.config.SerializerConfig;
 import com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder;
@@ -7,11 +27,8 @@
 import com.hazelcast.spi.serialization.SerializationService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.session.MapSession;
 
-import java.io.Serializable;
-import java.time.Duration;
-import java.time.Instant;
+import org.springframework.session.MapSession;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -22,11 +39,10 @@ class HazelcastSessionSerializerTests {
 	@BeforeEach
 	void setUp() {
 		SerializationConfig serializationConfig = new SerializationConfig();
-		SerializerConfig serializerConfig = new SerializerConfig()
-				.setImplementation(new HazelcastSessionSerializer())
+		SerializerConfig serializerConfig = new SerializerConfig().setImplementation(new HazelcastSessionSerializer())
 				.setTypeClass(MapSession.class);
 		serializationConfig.addSerializerConfig(serializerConfig);
-		serializationService = new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build();
+		this.serializationService = new DefaultSerializationServiceBuilder().setConfig(serializationConfig).build();
 	}
 
 	@Test
@@ -39,43 +55,45 @@ void serializeSessionWithStreamSerializer() {
 		originalSession.setLastAccessedTime(Instant.now());
 		originalSession.setId("custom-id");
 
-		Data serialized = serializationService.toData(originalSession);
-		MapSession cached = serializationService.toObject(serialized);
+		Data serialized = this.serializationService.toData(originalSession);
+		MapSession cached = this.serializationService.toObject(serialized);
 
 		assertThat(originalSession.getCreationTime()).isEqualTo(cached.getCreationTime());
 		assertThat(originalSession.getMaxInactiveInterval()).isEqualTo(cached.getMaxInactiveInterval());
 		assertThat(originalSession.getId()).isEqualTo(cached.getId());
 		assertThat(originalSession.getOriginalId()).isEqualTo(cached.getOriginalId());
 		assertThat(originalSession.getAttributeNames().size()).isEqualTo(cached.getAttributeNames().size());
-		assertThat(originalSession.getAttribute("attr1"))
-				.isEqualTo(cached.getAttribute("attr1"));
-		assertThat(originalSession.getAttribute("attr2"))
-				.isEqualTo(cached.getAttribute("attr2"));
+		assertThat(originalSession.getAttribute("attr1")).isEqualTo(cached.getAttribute("attr1"));
+		assertThat(originalSession.getAttribute("attr2")).isEqualTo(cached.getAttribute("attr2"));
 		assertThat(originalSession.getAttribute("attr3"))
 				.isEqualTo(cached.getAttribute("attr3"));
 	}
 
-
 	static class SerializableTestAttribute implements Serializable {
 
 		private int id;
 
-		public SerializableTestAttribute(int id) {
+		SerializableTestAttribute(int id) {
 			this.id = id;
 		}
 
 		@Override
 		public boolean equals(Object o) {
-			if (this == o) return true;
-			if (!(o instanceof SerializableTestAttribute)) return false;
+			if (this == o) {
+				return true;
+			}
+			if (!(o instanceof SerializableTestAttribute)) {
+				return false;
+			}
 			SerializableTestAttribute that = (SerializableTestAttribute) o;
-			return id == that.id;
+			return this.id == that.id;
 		}
 
 		@Override
 		public int hashCode() {
-			return id;
+			return this.id;
 		}
+
 	}
 
 }

From f1a832e0266172705ca1f6d8b0e3ad84d7a3ad19 Mon Sep 17 00:00:00 2001
From: Enes Ozcan 
Date: Tue, 8 Sep 2020 13:37:48 +0300
Subject: [PATCH 7/7] Add since tag.

---
 .../session/hazelcast/HazelcastSessionSerializer.java            | 1 +
 1 file changed, 1 insertion(+)

diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java
index 27c94cef7..d88df92b7 100644
--- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java
+++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionSerializer.java
@@ -79,6 +79,7 @@
  * 
* * @author Enes Ozcan + * @since 2.4.0 */ public class HazelcastSessionSerializer implements StreamSerializer {