From adf29e77020aa8a85c3dfae814a2dce9bf4fa391 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 27 Nov 2018 00:17:02 -0800 Subject: [PATCH] Refactor and simplify non-dirty Session handling. Resolves gh-16. --- ...rentSessionOperationsIntegrationTests.java | 5 +- ...essionAttributesDeltaIntegrationTests.java | 143 ++- ...essionAttributesDeltaIntegrationTests.java | 231 ++++ ...actGemFireOperationsSessionRepository.java | 425 ++++--- .../GemFireOperationsSessionRepository.java | 119 +- ...rializableSessionAttributesSerializer.java | 10 +- .../DataSerializableSessionSerializer.java | 16 +- ...mFireOperationsSessionRepositoryTests.java | 1107 +++++++++-------- ...mFireOperationsSessionRepositoryTests.java | 330 ++--- ...zableSessionAttributesSerializerTests.java | 10 +- ...ataSerializableSessionSerializerTests.java | 5 +- 11 files changed, 1311 insertions(+), 1090 deletions(-) create mode 100644 spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests.java diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractConcurrentSessionOperationsIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractConcurrentSessionOperationsIntegrationTests.java index 74c048d..d1b6573 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractConcurrentSessionOperationsIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/AbstractConcurrentSessionOperationsIntegrationTests.java @@ -140,8 +140,8 @@ public abstract class AbstractConcurrentSessionOperationsIntegrationTests extend waitForTick(2); assertTick(2); - // Save Session with no changes, no delta - assertThat(session instanceof GemFireSession && ((GemFireSession) session).isDirty()).isFalse(); + // Save Session with no changes/no delta + assertThat(session instanceof GemFireSession && ((GemFireSession) session).hasDelta()).isFalse(); save(session); } @@ -166,6 +166,7 @@ public abstract class AbstractConcurrentSessionOperationsIntegrationTests extend assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo", "attributeThree"); assertThat(session.getAttribute("attributeThree")).isEqualTo("three"); + assertThat(session instanceof GemFireSession && ((GemFireSession) session).hasDelta()).isTrue(); save(session); } diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests.java index 7bd0b35..d5c4de9 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests.java @@ -18,8 +18,10 @@ package org.springframework.session.data.gemfire; import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,6 +30,8 @@ import edu.umd.cs.mtc.TestFramework; import org.apache.geode.cache.client.ClientRegionShortcut; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; import org.springframework.session.Session; import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; @@ -36,7 +40,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; /** - * Integration tests containing test cases asserting the the proper behavior of concurrently accessing a Session + * Integration tests containing test cases asserting the the proper behavior of concurrently accessing a {@link Session} * using Spring Session backed by Apache Geode or Pivotal GemFire in a Multi-Threaded context. * * @author John Blum @@ -48,26 +52,33 @@ import org.springframework.test.context.junit4.SpringRunner; * @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration * @see org.springframework.test.context.ContextConfiguration * @see org.springframework.test.context.junit4.SpringRunner - * @since 2.1.1 + * @since 2.1.2 */ @RunWith(SpringRunner.class) -@ContextConfiguration +@ContextConfiguration( + classes = MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests.GemFireClientConfiguration.class +) public class MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests extends AbstractGemFireIntegrationTests { + @BeforeClass + public static void startGemFireServer() throws IOException { + startGemFireServer(GemFireServerConfiguration.class); + } + @Test - public void multiThreadedSessionOperationsAreCorrect() throws Throwable { - TestFramework.runOnce(new MultiThreadedSessionOperationsTestCase(this)); + public void multiThreadedConcurrentSessionOperationsAreCorrect() throws Throwable { + TestFramework.runOnce(new MultiThreadedConcurrentSessionOperationsTestCase(this)); } @SuppressWarnings("unused") - public static class MultiThreadedSessionOperationsTestCase extends MultithreadedTestCase { + public static final class MultiThreadedConcurrentSessionOperationsTestCase extends MultithreadedTestCase { private final AtomicReference sessionId = new AtomicReference<>(null); private final MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests testInstance; - public MultiThreadedSessionOperationsTestCase( + public MultiThreadedConcurrentSessionOperationsTestCase( MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests testInstance) { this.testInstance = testInstance; @@ -98,51 +109,21 @@ public class MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests assertThat(session.isExpired()).isFalse(); assertThat(session.getAttributeNames()).isEmpty(); - session.setAttribute("attributeOne", "foo"); - save(session); - Session loadedSession = findById(session.getId()); - - assertThat(loadedSession).isNotNull(); - assertThat(loadedSession.getId()).isEqualTo(session.getId()); - assertThat(loadedSession.isExpired()).isFalse(); - assertThat(loadedSession.getAttributeNames()).containsExactly("attributeOne"); - assertThat(loadedSession.getAttribute("attributeOne")).isEqualTo("foo"); - - loadedSession.setAttribute("attributeTwo", "bar"); - - save(loadedSession); - - this.sessionId.set(loadedSession.getId()); + this.sessionId.set(session.getId()); waitForTick(2); assertTick(2); - Session reloadedSession = findById(loadedSession.getId()); + session.setAttribute("attributeOne", "foo"); + session.setAttribute("attributeTwo", "bar"); - assertThat(reloadedSession).isNotNull(); - assertThat(reloadedSession.getId()).isEqualTo(loadedSession.getId()); - assertThat(reloadedSession.isExpired()).isFalse(); - assertThat(reloadedSession.getAttributeNames()) - .containsOnly("attributeOne", "attributeTwo", "attributeThree"); - assertThat(reloadedSession.getAttribute("attributeOne")).isEqualTo("foo"); - assertThat(reloadedSession.getAttribute("attributeTwo")).isEqualTo("bar"); - assertThat(reloadedSession.getAttribute("attributeThree")).isEqualTo("baz"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(session.getAttribute("attributeTwo")).isEqualTo("bar"); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); - waitForTick(4); - assertTick(4); - - Session endSession = findById(reloadedSession.getId()); - - assertThat(endSession).isNotNull(); - assertThat(endSession.getId()).isEqualTo(reloadedSession.getId()); - assertThat(endSession.isExpired()).isFalse(); - assertThat(endSession.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); - assertThat(endSession.getAttributeNames()).doesNotContain("attributeTwo"); - assertThat(endSession.getAttribute("attributeOne")).isEqualTo("foo"); - assertThat(endSession.getAttribute("attributeTwo")).isNull(); - assertThat(endSession.getAttribute("attributeThree")).isEqualTo("baz"); + save(session); } public void thread2() { @@ -157,32 +138,35 @@ public class MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests assertThat(session).isNotNull(); assertThat(session.getId()).isEqualTo(this.sessionId.get()); assertThat(session.isExpired()).isFalse(); - assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); - assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); - assertThat(session.getAttribute("attributeTwo")).isEqualTo("bar"); + assertThat(session.getAttributeNames()).isEmpty(); + + waitForTick(2); + assertTick(2); session.setAttribute("attributeThree", "baz"); + assertThat(session.getAttributeNames()).containsOnly("attributeThree"); + assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); + save(session); waitForTick(4); assertTick(4); - Session endSession = findById(session.getId()); + assertThat(session.getAttributeNames()).containsOnly("attributeThree"); - assertThat(endSession).isNotNull(); - assertThat(endSession.getId()).isEqualTo(session.getId()); - assertThat(endSession.isExpired()).isFalse(); - assertThat(endSession.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); - assertThat(endSession.getAttributeNames()).doesNotContain("attributeTwo"); - assertThat(endSession.getAttribute("attributeOne")).isEqualTo("foo"); - assertThat(endSession.getAttribute("attributeTwo")).isNull(); - assertThat(endSession.getAttribute("attributeThree")).isEqualTo("baz"); + session.setAttribute("attributeFour", "qux"); + + assertThat(session.getAttributeNames()).containsOnly("attributeThree", "attributeFour"); + assertThat(session.getAttribute("attributeFour")).isEqualTo("qux"); + assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); + + save(session); } public void thread3() { - Thread.currentThread().setName("User Session Three"); + Thread.currentThread().setName("User Session Four"); waitForTick(3); assertTick(3); @@ -197,7 +181,16 @@ public class MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests assertThat(session.getAttribute("attributeTwo")).isEqualTo("bar"); assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); - session.setAttribute("attributeTwo", null); + waitForTick(4); + assertTick(4); + + session.setAttribute("attributeThree", "bazatch"); + session.removeAttribute("attributeTwo"); + + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(session.getAttribute("attributeTwo")).isNull(); + assertThat(session.getAttribute("attributeThree")).isEqualTo("bazatch"); save(session); } @@ -205,25 +198,47 @@ public class MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests @Override public void finish() { + super.finish(); + Session session = findById(this.sessionId.get()); assertThat(session).isNotNull(); assertThat(session.getId()).isEqualTo(this.sessionId.get()); assertThat(session.isExpired()).isFalse(); - assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); - assertThat(session.getAttributeNames()).doesNotContain("attributeTwo"); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeThree", "attributeFour"); assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); assertThat(session.getAttribute("attributeTwo")).isNull(); - assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); + assertThat(session.getAttribute("attributeThree")).isEqualTo("bazatch"); + assertThat(session.getAttribute("attributeFour")).isEqualTo("qux"); } } - @ClientCacheApplication(logLevel = "error") + @ClientCacheApplication(logLevel = "error", subscriptionEnabled = true) @EnableGemFireHttpSession( - clientRegionShortcut = ClientRegionShortcut.LOCAL, + clientRegionShortcut = ClientRegionShortcut.PROXY, poolName = "DEFAULT", + regionName = "Sessions", sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME ) - static class TestConfiguration { } + static class GemFireClientConfiguration { } + + @CacheServerApplication( + name = "MultiThreadedClientServerHttpSessionAttributesDeltaIntegrationTests", + logLevel = "error" + ) + @EnableGemFireHttpSession( + regionName = "Sessions", + sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME + ) + static class GemFireServerConfiguration { + + public static void main(String[] args) { + + AnnotationConfigApplicationContext applicationContext = + new AnnotationConfigApplicationContext(GemFireServerConfiguration.class); + + applicationContext.registerShutdownHook(); + } + } } diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests.java new file mode 100644 index 0000000..d5d61cd --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests.java @@ -0,0 +1,231 @@ +/* + * Copyright 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.data.gemfire; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import edu.umd.cs.mtc.MultithreadedTestCase; +import edu.umd.cs.mtc.TestFramework; + +import org.apache.geode.cache.client.ClientRegionShortcut; + +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.session.Session; +import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession; +import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests containing test cases asserting the the proper behavior of concurrently accessing a {@link Session} + * using Spring Session backed by Apache Geode or Pivotal GemFire in a Multi-Threaded context. + * + * @author John Blum + * @see org.junit.Test + * @see edu.umd.cs.mtc.MultithreadedTestCase + * @see edu.umd.cs.mtc.TestFramework + * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession + * @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration + * @see org.springframework.test.context.ContextConfiguration + * @see org.springframework.test.context.junit4.SpringRunner + * @since 2.1.1 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests + extends AbstractGemFireIntegrationTests { + + @Test + public void multiThreadedSessionOperationsAreCorrect() throws Throwable { + TestFramework.runOnce(new MultiThreadedSessionOperationsTestCase(this)); + } + + @SuppressWarnings("unused") + public static class MultiThreadedSessionOperationsTestCase extends MultithreadedTestCase { + + private final AtomicReference sessionId = new AtomicReference<>(null); + + private final MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests testInstance; + + public MultiThreadedSessionOperationsTestCase( + MultiThreadedLocalClientHttpSessionAttributesDeltaIntegrationTests testInstance) { + + this.testInstance = testInstance; + } + + private Session findById(String id) { + return this.testInstance.get(id); + } + + private Session newSession() { + return this.testInstance.createSession(); + } + + private T save(T session) { + return this.testInstance.save(this.testInstance.touch(session)); + } + + public void thread1() { + + Thread.currentThread().setName("User Session One"); + + assertTick(0); + + Session session = newSession(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isNotEmpty(); + assertThat(session.isExpired()).isFalse(); + assertThat(session.getAttributeNames()).isEmpty(); + + session.setAttribute("attributeOne", "foo"); + + save(session); + + Session loadedSession = findById(session.getId()); + + assertThat(loadedSession).isNotNull(); + assertThat(loadedSession).isNotSameAs(session); + assertThat(loadedSession.getId()).isEqualTo(session.getId()); + assertThat(loadedSession.isExpired()).isFalse(); + assertThat(loadedSession.getAttributeNames()).containsExactly("attributeOne"); + assertThat(loadedSession.getAttribute("attributeOne")).isEqualTo("foo"); + + loadedSession.setAttribute("attributeTwo", "bar"); + + save(loadedSession); + + this.sessionId.set(loadedSession.getId()); + + waitForTick(2); + assertTick(2); + + Session reloadedSession = findById(loadedSession.getId()); + + assertThat(reloadedSession).isNotNull(); + assertThat(reloadedSession).isNotSameAs(loadedSession); + assertThat(reloadedSession.getId()).isEqualTo(loadedSession.getId()); + assertThat(reloadedSession.isExpired()).isFalse(); + assertThat(reloadedSession.getAttributeNames()) + .containsOnly("attributeOne", "attributeTwo", "attributeThree"); + assertThat(reloadedSession.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(reloadedSession.getAttribute("attributeTwo")).isEqualTo("bar"); + assertThat(reloadedSession.getAttribute("attributeThree")).isEqualTo("baz"); + + waitForTick(4); + assertTick(4); + + Session endSession = findById(reloadedSession.getId()); + + assertThat(endSession).isNotNull(); + assertThat(endSession.getId()).isEqualTo(reloadedSession.getId()); + assertThat(endSession.isExpired()).isFalse(); + assertThat(endSession.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); + assertThat(endSession.getAttributeNames()).doesNotContain("attributeTwo"); + assertThat(endSession.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(endSession.getAttribute("attributeTwo")).isNull(); + assertThat(endSession.getAttribute("attributeThree")).isEqualTo("baz"); + } + + public void thread2() { + + Thread.currentThread().setName("User Session Two"); + + waitForTick(1); + assertTick(1); + + Session session = findById(this.sessionId.get()); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(this.sessionId.get()); + assertThat(session.isExpired()).isFalse(); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(session.getAttribute("attributeTwo")).isEqualTo("bar"); + + session.setAttribute("attributeThree", "baz"); + + save(session); + + waitForTick(4); + assertTick(4); + + Session endSession = findById(session.getId()); + + assertThat(endSession).isNotNull(); + assertThat(endSession.getId()).isEqualTo(session.getId()); + assertThat(endSession.isExpired()).isFalse(); + assertThat(endSession.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); + assertThat(endSession.getAttributeNames()).doesNotContain("attributeTwo"); + assertThat(endSession.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(endSession.getAttribute("attributeTwo")).isNull(); + assertThat(endSession.getAttribute("attributeThree")).isEqualTo("baz"); + } + + public void thread3() { + + Thread.currentThread().setName("User Session Three"); + + waitForTick(3); + assertTick(3); + + Session session = findById(this.sessionId.get()); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(this.sessionId.get()); + assertThat(session.isExpired()).isFalse(); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo", "attributeThree"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(session.getAttribute("attributeTwo")).isEqualTo("bar"); + assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); + + session.setAttribute("attributeTwo", null); + + save(session); + } + + @Override + public void finish() { + + Session session = findById(this.sessionId.get()); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isEqualTo(this.sessionId.get()); + assertThat(session.isExpired()).isFalse(); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeThree"); + assertThat(session.getAttributeNames()).doesNotContain("attributeTwo"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("foo"); + assertThat(session.getAttribute("attributeTwo")).isNull(); + assertThat(session.getAttribute("attributeThree")).isEqualTo("baz"); + } + } + + @ClientCacheApplication(logLevel = "error", copyOnRead = true) + @EnableGemFireHttpSession( + clientRegionShortcut = ClientRegionShortcut.LOCAL, + poolName = "DEFAULT", + sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME + ) + static class TestConfiguration { } + +} diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java index 16b4c72..3103196 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepository.java @@ -16,7 +16,6 @@ package org.springframework.session.data.gemfire; -import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; import java.io.DataInput; @@ -28,7 +27,6 @@ import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Optional; @@ -56,6 +54,8 @@ import org.springframework.data.gemfire.GemfireAccessor; import org.springframework.data.gemfire.GemfireOperations; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import org.springframework.session.SessionRepository; @@ -106,7 +106,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi implements ApplicationEventPublisherAware, FindByIndexNameSessionRepository, InitializingBean { private static final AtomicBoolean usingDataSerialization = new AtomicBoolean(false); - private static final AtomicBoolean usingEagerCommit = new AtomicBoolean(false); private ApplicationEventPublisher applicationEventPublisher = event -> {}; @@ -284,25 +283,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return usingDataSerialization.get(); } - /** - * Set a condition to determine whether a {@link Session} is copied and committed before saving. - * - * @param useEagerCommit boolean to determine whether to copy and commit a {@link Session} before saving. - */ - @SuppressWarnings("unused") - public void setUseEagerCommit(boolean useEagerCommit) { - usingEagerCommit.set(useEagerCommit); - } - - /** - * Determines whether a {@link Session} is copied and committed before saving. - * - * @return a boolean value indicating whether a {@link Session} is copied and committed before saving. - */ - protected boolean isUsingEagerCommit() { - return usingEagerCommit.get(); - } - /** * Callback method during Spring bean initialization that will capture the fully-qualified name * of the cache {@link Region} used to manage {@link Session} state and register this {@link SessionRepository} @@ -375,7 +355,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi } /** - * Rememvers the given {@link Object session ID}. + * Remembers the given {@link Object session ID}. * * @param sessionId {@link Object} containing the session ID to remember. * @return a boolean value whether Spring Session is interested in and will remember @@ -402,7 +382,8 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi */ Session toSession(Object obj, String sessionId) { - return obj instanceof Session ? (Session) obj + return obj instanceof Session + ? (Session) obj : Optional.ofNullable(sessionId) .filter(StringUtils::hasText) .map(SessionIdHolder::create) @@ -467,6 +448,25 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi }); } + /** + * Commits the given {@link Session}. + * + * @param session {@link Session} to commit, if committable. + * @return the given {@link Session} + * @see GemFireSession#commit() + */ + protected @Nullable Session commit(@Nullable Session session) { + + return Optional.ofNullable(session) + .filter(GemFireSession.class::isInstance) + .map(GemFireSession.class::cast) + .map(gemfireSession -> { + gemfireSession.commit(); + return gemfireSession; + }) + .orElse(session); + } + /** * Deletes the given {@link Session} from GemFire. * @@ -476,7 +476,9 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi * @see #deleteById(String) */ protected Session delete(Session session) { + deleteById(session.getId()); + return null; } @@ -579,12 +581,12 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi /** * Updates the {@link Session#setLastAccessedTime(Instant)} property of the {@link Session}. * - * @param {@link Class} sub-type of the {@link Session}. * @param session {@link Session} to touch. * @return the {@link Session}. * @see org.springframework.session.Session#setLastAccessedTime(Instant) + * @see java.time.Instant#now() */ - protected T touch(T session) { + protected Session touch(Session session) { session.setLastAccessedTime(Instant.now()); @@ -616,7 +618,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi out.writeLong(getLastAccessedTime().toEpochMilli()); out.writeLong(getMaxInactiveInterval().getSeconds()); getAttributes().toDelta(out); - clearDelta(); } public synchronized void fromDelta(DataInput in) throws IOException { @@ -625,7 +626,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi setLastAccessedTime(Instant.ofEpochMilli(in.readLong())); setMaxInactiveInterval(Duration.ofSeconds(in.readLong())); getAttributes().fromDelta(in); - clearDelta(); } } @@ -641,52 +641,33 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi protected static final Duration DEFAULT_MAX_INACTIVE_INTERVAL = Duration.ZERO; - protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; - - private static final String GEMFIRE_SESSION_TO_STRING = + protected static final String GEMFIRE_SESSION_TO_STRING = "{ @type = %1$s, id = %2$s, creationTime = %3$s, lastAccessedTime = %4$s, maxInactiveInterval = %5$s, principalName = %6$s }"; - private transient boolean delta = false; - private transient boolean dirty = false; - - private Duration maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL; - - private final Instant creationTime; - private Instant lastAccessedTime; - - private transient final SpelExpressionParser parser = new SpelExpressionParser(); - - private String id; - - private transient final T sessionAttributes = newSessionAttributes(this); - - protected GemFireSession() { - this(generateId()); - } - - protected GemFireSession(String id) { - - this.id = validateId(id); - this.creationTime = Instant.now(); - this.dirty = true; - this.lastAccessedTime = this.creationTime; - } - - protected GemFireSession(Session session) { - - Assert.notNull(session, "The Session to copy must not be null"); - - this.id = session.getId(); - this.creationTime = session.getCreationTime(); - this.lastAccessedTime = session.getLastAccessedTime(); - this.maxInactiveInterval = session.getMaxInactiveInterval(); - this.sessionAttributes.from(session); - } + protected static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + /** + * Factory method used to create a new instance of {@link GemFireSession} initialized with + * the {@link #DEFAULT_MAX_INACTIVE_INTERVAL}. + * + * @return new {@link GemFireSession}. + * @see #create(Duration) + */ public static GemFireSession create() { return create(DEFAULT_MAX_INACTIVE_INTERVAL); } + /** + * Factory method used to create a new instance of {@link GemFireSession} initialized with + * the given {@link Duration max inactive interval}. + * + * @param maxInactiveInterval {@link Duration} specifying the max inactive interval before + * this {@link Session} will expire. + * @return a new instance of {@link GemFireSession} initialized with + * the given {@link Duration max inactive interval}. + * @see #isUsingDataSerialization() + * @see java.time.Duration + */ public static GemFireSession create(Duration maxInactiveInterval) { GemFireSession session = isUsingDataSerialization() @@ -698,48 +679,94 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return session; } - public static GemFireSession copy(Session session) { + /** + * Copy (i.e. clone) the given {@link Session}. + * + * @param session {@link Session} to copy/clone. + * @return a new instance of {@link GemFireSession} copied from the given {@link Session}. + * @see org.springframework.session.Session + * @see #isUsingDataSerialization() + */ + public static GemFireSession copy(@NonNull Session session) { return isUsingDataSerialization() ? new DeltaCapableGemFireSession(session) : new GemFireSession(session); } - @SuppressWarnings("all") - public static GemFireSession copyCommitted(Session session) { - - synchronized (session) { - - GemFireSession sessionCopy = copy(session); - - if (session instanceof GemFireSession) { - ((GemFireSession) session).commit(); - } - - return sessionCopy; - } - } - + /** + * Returns the given {@link Session} if the {@link Session} is a {@link GemFireSession} or return a copy + * of the given {@link Session} as a {@link GemFireSession}. + * + * @param {@link Class sub-type} of {@link GemFireSession}. + * @param session {@link Session} to evaluate and possibly copy. + * @return the given {@link Session} if the {@link Session} is a {@link GemFireSession} or return a copy + * of the given {@link Session} as a {@link GemFireSession} + * @see #copy(Session) + */ @SuppressWarnings("unchecked") - public static T from(Session session) { + public static T from(@NonNull Session session) { return (T) (session instanceof GemFireSession ? session : copy(session)); } + private transient boolean delta = true; + + private Duration maxInactiveInterval; + + private final Instant creationTime; + private Instant lastAccessedTime; + + private transient final SpelExpressionParser parser = new SpelExpressionParser(); + + private String id; + + private transient final T sessionAttributes = newSessionAttributes(this); + /** - * Randomly generates a unique identifier (ID) from {@link UUID} to be used as the {@link Session} ID. + * Constructs a new, default instance of {@link GemFireSession} initialized with + * a generated {@link Session#getId() Session Identifier}. * - * @return a new unique identifier (ID). - * @see java.util.UUID#randomUUID() + * @see #GemFireSession(String) + * @see #generateSessionId() */ - private static String generateId() { - return UUID.randomUUID().toString(); + protected GemFireSession() { + this(generateSessionId()); } - private static String validateId(String id) { + /** + * Constructs a new instance of {@link GemFireSession} initialized with + * the given {@link Session#getId() Session Identifier}. + * + * Additionally, the {@link #creationTime} is set to {@link Instant#now()}, {@link #lastAccessedTime} + * is set to {@link #creationTime} and the {@link #maxInactiveInterval} is set to {@link Duration#ZERO}. + * + * @param id {@link String} containing the unique identifier for this {@link Session}. + * @see #validateSessionId(String) + */ + protected GemFireSession(String id) { - return Optional.ofNullable(id) - .filter(StringUtils::hasText) - .orElseThrow(() -> newIllegalArgumentException("ID is required")); + this.id = validateSessionId(id); + this.creationTime = Instant.now(); + this.lastAccessedTime = this.creationTime; + this.maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL; + } + + /** + * Constructs a new instance of {@link GemFireSession} copied from the given {@link Session}. + * + * @param session {@link Session} to copy. + * @throws IllegalArgumentException if {@link Session} is {@literal null}. + * @see org.springframework.session.Session + */ + protected GemFireSession(Session session) { + + Assert.notNull(session, "The Session to copy must not be null"); + + this.id = session.getId(); + this.creationTime = session.getCreationTime(); + this.lastAccessedTime = session.getLastAccessedTime(); + this.maxInactiveInterval = session.getMaxInactiveInterval(); + this.sessionAttributes.from(session); } /** @@ -754,19 +781,52 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return (T) new GemFireSessionAttributes(lock); } + /** + * Change the {@link String identifier} of this {@link Session}. + * + * @return the new {@link String identifier} of of this {@link Session}. + * @see #generateSessionId() + * @see #triggerDelta() + * @see #getId() + */ @Override public synchronized String changeSessionId() { - this.id = generateId(); + this.id = generateSessionId(); - markDirty(); triggerDelta(); return getId(); } - public synchronized void clearDelta() { + /** + * Randomly generates a {@link String unique identifier} (ID) from {@link UUID} to be used as + * the {@link Session#getId() ID} for this {@link Session}. + * + * @return a new {@link String unique identifier (ID)}. + * @see java.util.UUID#randomUUID() + */ + private static String generateSessionId() { + return UUID.randomUUID().toString(); + } + + /** + * Validates the given {@link Session} {@link String identifier} (ID) is set and valid. + * + * @param id {@link String} containing the {@link Session} identifier. + * @return the given {@link String ID}. + * @throws IllegalArgumentException if {@link String} contains no value. + */ + private static String validateSessionId(String id) { + + Assert.hasText(id, "ID is required"); + + return id; + } + + protected synchronized void commit() { this.delta = false; + getAttributes().commit(); } public synchronized boolean hasDelta() { @@ -777,29 +837,12 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi triggerDelta(true); } - protected synchronized void triggerDelta(boolean condition) { - this.delta |= condition; - } - - public synchronized void commit() { - this.dirty = false; - getAttributes().commit(); - } - - protected synchronized boolean isDirty() { - return this.dirty || getAttributes().isDirty(); - } - - protected synchronized void markDirty() { - markDirty(true); - } - - protected synchronized void markDirty(boolean dirty) { - this.dirty |= dirty; + protected synchronized void triggerDelta(boolean delta) { + this.delta |= delta; } synchronized void setId(String id) { - this.id = validateId(id); + this.id = validateSessionId(id); } public synchronized String getId() { @@ -850,10 +893,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi public synchronized void setLastAccessedTime(Instant lastAccessedTime) { - boolean changed = !ObjectUtils.nullSafeEquals(this.lastAccessedTime, lastAccessedTime); - - markDirty(changed); - triggerDelta(changed); + triggerDelta(!ObjectUtils.nullSafeEquals(this.lastAccessedTime, lastAccessedTime)); this.lastAccessedTime = lastAccessedTime; } @@ -862,18 +902,17 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return this.lastAccessedTime; } - public synchronized void setMaxInactiveInterval(Duration maxInactiveIntervalInSeconds) { + public synchronized void setMaxInactiveInterval(Duration maxInactiveInterval) { - boolean changed = !ObjectUtils.nullSafeEquals(this.maxInactiveInterval, maxInactiveIntervalInSeconds); + triggerDelta(!ObjectUtils.nullSafeEquals(this.maxInactiveInterval, maxInactiveInterval)); - markDirty(changed); - triggerDelta(changed); - - this.maxInactiveInterval = maxInactiveIntervalInSeconds; + this.maxInactiveInterval = maxInactiveInterval; } public synchronized Duration getMaxInactiveInterval() { - return Optional.ofNullable(this.maxInactiveInterval).orElse(DEFAULT_MAX_INACTIVE_INTERVAL); + + return Optional.ofNullable(this.maxInactiveInterval) + .orElse(DEFAULT_MAX_INACTIVE_INTERVAL); } public synchronized void setPrincipalName(String principalName) { @@ -950,6 +989,14 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi super(lock); } + protected Map getSessionAttributeDeltas() { + + synchronized (getLock()) { + return this.sessionAttributeDeltas; + } + } + + @Override public Object setAttribute(String attributeName, Object attributeValue) { synchronized (getLock()) { @@ -959,7 +1006,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi Object previousAttributeValue = super.setAttribute(attributeName, attributeValue); if (!attributeValue.equals(previousAttributeValue)) { - this.sessionAttributeDeltas.put(attributeName, attributeValue); + getSessionAttributeDeltas().put(attributeName, attributeValue); } return previousAttributeValue; @@ -977,7 +1024,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return Optional.ofNullable(super.removeAttribute(attributeName)) .map(previousAttributeValue -> { - this.sessionAttributeDeltas.put(attributeName, null); + getSessionAttributeDeltas().put(attributeName, null); return previousAttributeValue; }) .orElse(null); @@ -988,14 +1035,14 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi synchronized (getLock()) { - out.writeInt(this.sessionAttributeDeltas.size()); + Map sessionAttributeDeltas = getSessionAttributeDeltas(); - for (Map.Entry entry : this.sessionAttributeDeltas.entrySet()) { + out.writeInt(sessionAttributeDeltas.size()); + + for (Entry entry : sessionAttributeDeltas.entrySet()) { out.writeUTF(entry.getKey()); writeObject(entry.getValue(), out); } - - clearDelta(); } } @@ -1007,7 +1054,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi public boolean hasDelta() { synchronized (getLock()) { - return !this.sessionAttributeDeltas.isEmpty(); + return !getSessionAttributeDeltas().isEmpty(); } } @@ -1024,9 +1071,11 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi deltas.put(in.readUTF(), readObject(in)); } + Map sessionAttributeDeltas = getSessionAttributeDeltas(); + deltas.forEach((key, value) -> { setAttribute(key, value); - this.sessionAttributeDeltas.remove(key); + sessionAttributeDeltas.remove(key); }); } catch (ClassNotFoundException cause) { @@ -1040,16 +1089,17 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi } @Override - public void clearDelta() { + protected void commit() { synchronized (getLock()) { - this.sessionAttributeDeltas.clear(); + getSessionAttributeDeltas().clear(); + super.commit(); } } } /** - * The GemFireSessionAttributes class is a container for Session attributes implementing + * The {@link GemFireSessionAttributes} class is a container for Session attributes implementing * both the {@link DataSerializable} and {@link Delta} Pivotal GemFire interfaces for efficient * storage and distribution (replication) in GemFire. Additionally, GemFireSessionAttributes * extends {@link AbstractMap} providing {@link Map}-like behavior since attributes of a Session @@ -1063,20 +1113,6 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi @SuppressWarnings("serial") public static class GemFireSessionAttributes extends AbstractMap { - private volatile transient boolean dirty = false; - - private transient final Map sessionAttributes = new HashMap<>(); - - private transient final Object lock; - - protected GemFireSessionAttributes() { - this.lock = this; - } - - protected GemFireSessionAttributes(Object lock) { - this.lock = lock != null ? lock : this; - } - public static GemFireSessionAttributes create() { return new GemFireSessionAttributes(); } @@ -1085,7 +1121,38 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi return new GemFireSessionAttributes(lock); } - protected Object getLock() { + private transient boolean delta = false; + + private transient final Map sessionAttributes = new HashMap<>(); + + private transient final Object lock; + + /** + * Constructs a new instance of {@link GemFireSessionAttributes}. + */ + protected GemFireSessionAttributes() { + this.lock = this; + } + + /** + * Constructs a new instance of {@link GemFireSessionAttributes} initialized with the given {@link Object lock} + * to use to guard against concurrent access by multiple {@link Thread Threads}. + * + * @param lock {@link Object} used as the {@literal mutex} to guard the operations of this object + * from concurrent access by multiple {@link Thread Threads}. + */ + protected GemFireSessionAttributes(@Nullable Object lock) { + this.lock = lock != null ? lock : this; + } + + /** + * Returns the {@link Object} used as the {@literal lock} guarding the methods of this object + * from concurrent access by multiple {@link Thread Threads}. + * + * @return the {@link Object lock} guarding the methods of this object from concurrent access + * by multiple {@link Thread Threads}. + */ + public Object getLock() { return this.lock; } @@ -1102,7 +1169,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi Object previousAttributeValue = this.sessionAttributes.put(attributeName, attributeValue); - this.dirty |= !attributeValue.equals(previousAttributeValue); + this.delta |= !attributeValue.equals(previousAttributeValue); return previousAttributeValue; } @@ -1111,7 +1178,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi synchronized (getLock()) { - this.dirty |= this.sessionAttributes.containsKey(attributeName); + this.delta |= this.sessionAttributes.containsKey(attributeName); return this.sessionAttributes.remove(attributeName); } @@ -1128,7 +1195,7 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi public Set getAttributeNames() { synchronized (getLock()) { - return Collections.unmodifiableSet(new HashSet<>(this.sessionAttributes.keySet())); + return Collections.unmodifiableSet(this.sessionAttributes.keySet()); } } @@ -1136,25 +1203,29 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi @SuppressWarnings("all") public Set> entrySet() { - return new AbstractSet>() { + synchronized (getLock()) { - @Override - public Iterator> iterator() { - return Collections.unmodifiableMap(GemFireSessionAttributes.this.sessionAttributes) - .entrySet().iterator(); - } + return new AbstractSet>() { - @Override - public int size() { - return GemFireSessionAttributes.this.sessionAttributes.size(); - } - }; + @Override + public Iterator> iterator() { + return Collections.unmodifiableMap(GemFireSessionAttributes.this.sessionAttributes) + .entrySet().iterator(); + } + + @Override + public int size() { + return GemFireSessionAttributes.this.sessionAttributes.size(); + } + }; + } } - public void clearDelta() { } + protected void commit() { - public void commit() { - this.dirty = false; + synchronized (getLock()) { + this.delta = false; + } } public void from(Session session) { @@ -1181,16 +1252,18 @@ public abstract class AbstractGemFireOperationsSessionRepository extends CacheLi } public boolean hasDelta() { - return false; - } - public boolean isDirty() { - return this.dirty; - } + synchronized (getLock()) { + return this.delta; + } + } @Override public String toString() { - return this.sessionAttributes.toString(); + + synchronized (getLock()) { + return this.sessionAttributes.toString(); + } } } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepository.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepository.java index 621b26b..c84dd18 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepository.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepository.java @@ -43,7 +43,7 @@ import org.springframework.session.SessionRepository; public class GemFireOperationsSessionRepository extends AbstractGemFireOperationsSessionRepository { // Pivotal GemFire OQL query used to lookup Sessions by arbitrary attributes. - protected static final String FIND_SESSIONS_BY_INDEX_NAME_INDEX_VALUE_QUERY = + protected static final String FIND_SESSIONS_BY_INDEX_NAME_AND_INDEX_VALUE_QUERY = "SELECT s FROM %1$s s WHERE s.attributes['%2$s'] = $1"; // Pivotal GemFire OQL query used to look up Sessions by principal name. @@ -63,6 +63,44 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation super(template); } + /** + * Constructs a new {@link Session} instance backed by GemFire. + * + * @return an instance of {@link Session} backed by GemFire. + * @see AbstractGemFireOperationsSessionRepository.GemFireSession#create(Duration) + * @see org.springframework.session.Session + * @see #getMaxInactiveIntervalInSeconds() + */ + @NonNull + public Session createSession() { + return GemFireSession.create(getMaxInactiveInterval()); + } + + /** + * Gets a copy of an existing, non-expired {@link Session} by ID. + * + * If the {@link Session} is expired, then the {@link Session }is deleted. + * + * @param sessionId a String indicating the ID of the Session to get. + * @return an existing {@link Session} by ID or null if no {@link Session} exists. + * @see AbstractGemFireOperationsSessionRepository.GemFireSession#from(Session) + * @see org.springframework.session.Session + * @see #deleteById(String) + */ + @Nullable + public Session findById(String sessionId) { + + Session storedSession = getTemplate().get(sessionId); + + if (storedSession != null) { + storedSession = storedSession.isExpired() + ? delete(storedSession) + : touch(commit(GemFireSession.from(storedSession))); + } + + return storedSession; + } + /** * Looks up all available Sessions with the particular attribute indexed by name * having the given value. @@ -99,47 +137,9 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation */ protected String prepareQuery(String indexName) { - return (PRINCIPAL_NAME_INDEX_NAME.equals(indexName) + return PRINCIPAL_NAME_INDEX_NAME.equals(indexName) ? String.format(FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, getFullyQualifiedRegionName()) - : String.format(FIND_SESSIONS_BY_INDEX_NAME_INDEX_VALUE_QUERY, getFullyQualifiedRegionName(), indexName)); - } - - /** - * Constructs a new {@link Session} instance backed by GemFire. - * - * @return an instance of {@link Session} backed by GemFire. - * @see AbstractGemFireOperationsSessionRepository.GemFireSession#create(Duration) - * @see org.springframework.session.Session - * @see #getMaxInactiveIntervalInSeconds() - */ - @NonNull - public Session createSession() { - return GemFireSession.create(getMaxInactiveInterval()); - } - - /** - * Gets a copy of an existing, non-expired {@link Session} by ID. - * - * If the {@link Session} is expired, then the {@link Session }is deleted. - * - * @param sessionId a String indicating the ID of the Session to get. - * @return an existing {@link Session} by ID or null if no {@link Session} exists. - * @see AbstractGemFireOperationsSessionRepository.GemFireSession#from(Session) - * @see org.springframework.session.Session - * @see #deleteById(String) - */ - @Nullable - public Session findById(String sessionId) { - - Session storedSession = getTemplate().get(sessionId); - - if (storedSession != null) { - storedSession = storedSession.isExpired() - ? delete(storedSession) - : touch(GemFireSession.from(storedSession)); - } - - return storedSession; + : String.format(FIND_SESSIONS_BY_INDEX_NAME_AND_INDEX_VALUE_QUERY, getFullyQualifiedRegionName(), indexName); } /** @@ -161,41 +161,16 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation } private boolean isDirty(@NonNull Session session) { - return !(session instanceof GemFireSession) || ((GemFireSession) session).isDirty(); + return !(session instanceof GemFireSession) || ((GemFireSession) session).hasDelta(); } - /*private*/ void doSave(@NonNull Session session) { + void doSave(@NonNull Session session) { - boolean usingEagerCommits = isUsingEagerCommit(); + // Save Session As GemFireSession + getTemplate().put(session.getId(), GemFireSession.from(session)); - GemFireSession sessionToSave = usingEagerCommits - ? GemFireSession.copyCommitted(session) - : GemFireSession.from(session); - - try { - // Save Session As GemFireSession - getTemplate().put(session.getId(), sessionToSave); - - if (isCommittable(usingEagerCommits, session)) { - ((GemFireSession) session).commit(); - } - } - catch (RuntimeException cause) { - - if (isEagerlyCommittable(usingEagerCommits, session)) { - ((GemFireSession) session).markDirty(); - } - - throw cause; - } - } - - private boolean isCommittable(boolean usingEagerCommits, @Nullable Session session) { - return !usingEagerCommits && session instanceof GemFireSession; - } - - private boolean isEagerlyCommittable(boolean usingEagerCommits, @Nullable Session session) { - return usingEagerCommits && session instanceof GemFireSession; + // Commit Session + commit(session); } /** diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java index 1571ca1..f529621 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializer.java @@ -37,10 +37,12 @@ import org.springframework.session.data.gemfire.serialization.data.AbstractDataS * framework. * * @author John Blum + * @see java.io.DataInput + * @see java.io.DataOutput * @see org.apache.geode.DataSerializer * @see org.springframework.session.Session - * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes * @see org.springframework.session.data.gemfire.serialization.SessionSerializer * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer * @since 2.0.0 @@ -85,7 +87,7 @@ public class DataSerializableSessionAttributesSerializer @Override public void serialize(GemFireSessionAttributes sessionAttributes, DataOutput out) { - synchronized (sessionAttributes) { + synchronized (sessionAttributes.getLock()) { Set attributeNames = nullSafeSet(sessionAttributes.getAttributeNames()); @@ -95,8 +97,6 @@ public class DataSerializableSessionAttributesSerializer safeWrite(out, output -> output.writeUTF(attributeName)); safeWrite(out, output -> serializeObject(sessionAttributes.getAttribute(attributeName), output)); }); - - sessionAttributes.clearDelta(); } } @@ -109,8 +109,6 @@ public class DataSerializableSessionAttributesSerializer sessionAttributes.setAttribute(safeRead(in, DataInput::readUTF), safeRead(in, this::deserializeObject)); } - sessionAttributes.clearDelta(); - return sessionAttributes; } } diff --git a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java index b0c5f81..79fa076 100644 --- a/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java +++ b/spring-session-data-geode/src/main/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializer.java @@ -45,10 +45,13 @@ import org.springframework.util.StringUtils; * @see java.io.DataOutput * @see org.apache.geode.DataSerializer * @see org.springframework.session.Session - * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSessionAttributes + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes * @see org.springframework.session.data.gemfire.serialization.SessionSerializer * @see org.springframework.session.data.gemfire.serialization.data.AbstractDataSerializableSessionSerializer + * @see org.springframework.session.data.gemfire.support.AbstractSession * @since 2.0.0 */ @SuppressWarnings("unused") @@ -101,18 +104,15 @@ public class DataSerializableSessionSerializer extends AbstractDataSerializableS String principalName = session.getPrincipalName(); - int length = StringUtils.hasText(principalName) ? principalName.length() : 0; + int principalNameLength = StringUtils.hasText(principalName) ? principalName.length() : 0; - safeWrite(out, output -> output.writeInt(length)); + safeWrite(out, output -> output.writeInt(principalNameLength)); - if (length > 0) { + if (principalNameLength > 0) { safeWrite(out, output -> output.writeUTF(principalName)); } safeWrite(out, output -> serializeObject(session.getAttributes(), output)); - - session.getAttributes().clearDelta(); - session.clearDelta(); } } @@ -154,8 +154,6 @@ public class DataSerializableSessionSerializer extends AbstractDataSerializableS } session.getAttributes().from(this.safeRead(in, this::deserializeObject)); - session.getAttributes().clearDelta(); - session.clearDelta(); return session; } diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java index 1ffe49a..bb64e98 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/AbstractGemFireOperationsSessionRepositoryTests.java @@ -43,7 +43,6 @@ import static org.springframework.session.data.gemfire.AbstractGemFireOperations import java.io.DataInput; import java.io.DataOutput; -import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.Arrays; @@ -54,6 +53,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; import org.junit.Test; @@ -111,13 +111,16 @@ import org.apache.commons.logging.Log; @RunWith(MockitoJUnitRunner.class) public class AbstractGemFireOperationsSessionRepositoryTests { - protected static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 600; + protected static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 300; private AbstractGemFireOperationsSessionRepository sessionRepository; @Mock private Log mockLog; + @Mock + private Region mockRegion; + @Mock private Session mockSession; @@ -125,11 +128,10 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @SuppressWarnings("all") public void setup() { - GemfireTemplate gemfireTemplate = new GemfireTemplate(mock(Region.class)); + GemfireTemplate gemfireTemplate = new GemfireTemplate(this.mockRegion); this.sessionRepository = spy(new TestGemFireOperationsSessionRepository(gemfireTemplate)); this.sessionRepository.setUseDataSerialization(false); - this.sessionRepository.setUseEagerCommit(false); doReturn(this.mockLog).when(this.sessionRepository).getLogger(); } @@ -184,26 +186,31 @@ public class AbstractGemFireOperationsSessionRepositoryTests { return mockSession; } - private Session mockSession(String sessionId, long creationAndLastAccessedTime, - long maxInactiveIntervalInSeconds) { - - return mockSession(sessionId, creationAndLastAccessedTime, creationAndLastAccessedTime, - maxInactiveIntervalInSeconds); + private Session mockSession(String sessionId, long creationAndLastAccessedTime, long maxInactiveInterval) { + return mockSession(sessionId, creationAndLastAccessedTime, creationAndLastAccessedTime, maxInactiveInterval); } - private Session mockSession(String sessionId, long creationTime, long lastAccessedTime, - long maxInactiveIntervalInSeconds) { + private Session mockSession(String sessionId, long creationTime, long lastAccessedTime, long maxInactiveInterval) { Session mockSession = mock(Session.class, sessionId); when(mockSession.getId()).thenReturn(sessionId); when(mockSession.getCreationTime()).thenReturn(Instant.ofEpochMilli(creationTime)); when(mockSession.getLastAccessedTime()).thenReturn(Instant.ofEpochMilli(lastAccessedTime)); - when(mockSession.getMaxInactiveInterval()).thenReturn(Duration.ofSeconds(maxInactiveIntervalInSeconds)); + when(mockSession.getMaxInactiveInterval()).thenReturn(Duration.ofSeconds(maxInactiveInterval)); return mockSession; } + private GemFireSession newNonDirtyGemFireSession() { + + GemFireSession session = GemFireSession.create(); + + session.commit(); + + return session; + } + private AbstractGemFireOperationsSessionRepository withRegion( AbstractGemFireOperationsSessionRepository sessionRepository, Region region) { @@ -341,20 +348,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(GemFireOperationsSessionRepository.isUsingDataSerialization()).isFalse(); } - @Test - public void setAndIsUsingEagerCommit() { - - assertThat(this.sessionRepository.isUsingEagerCommit()).isFalse(); - - this.sessionRepository.setUseEagerCommit(true); - - assertThat(this.sessionRepository.isUsingEagerCommit()).isTrue(); - - this.sessionRepository.setUseEagerCommit(false); - - assertThat(this.sessionRepository.isUsingEagerCommit()).isFalse(); - } - @Test public void isCreateWithCreateOperationReturnsTrue() { @@ -1080,6 +1073,31 @@ public class AbstractGemFireOperationsSessionRepositoryTests { .publishEvent(isA(SessionExpiredEvent.class)); } + @Test + public void commitGemFireSessionIsCorrect() { + + GemFireSession session = spy(GemFireSession.create()); + + assertThat(session).isNotNull(); + assertThat(session.hasDelta()).isTrue(); + + this.sessionRepository.commit(session); + + assertThat(session.hasDelta()).isFalse(); + + verify(session, times(1)).commit(); + } + + @Test + public void commitNonGemFireSessionIsSafe() { + this.sessionRepository.commit(this.mockSession); + } + + @Test + public void commitNullIsSafe() { + this.sessionRepository.commit(null); + } + @Test public void deleteSessionCallsDeleteSessionId() { @@ -1154,38 +1172,42 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void touchSetsLastAccessedTime() { + assertThat(this.sessionRepository.touch(this.mockSession)).isSameAs(this.mockSession); + verify(this.mockSession, times(1)).setLastAccessedTime(any(Instant.class)); } @Test public void constructDefaultGemFireSession() { - Instant beforeOrAtCreationTime = Instant.now(); + Instant testCreationTime = Instant.now(); GemFireSession session = new GemFireSession(); assertThat(session.getId()).isNotNull(); - assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(session.getLastAccessedTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.hasDelta()).isTrue(); + assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); - assertThat(session.getAttributeNames()).isNotNull(); - assertThat(session.getAttributeNames()).isEmpty(); + assertThat(session.getAttributes()).isNotNull(); + assertThat(session.getAttributes()).isEmpty(); } @Test public void constructGemFireSessionWithId() { - Instant beforeOrAtCreationTime = Instant.now(); + Instant testCreationTime = Instant.now(); GemFireSession session = new GemFireSession("1"); assertThat(session.getId()).isEqualTo("1"); - assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(session.getLastAccessedTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.hasDelta()).isTrue(); + assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); - assertThat(session.getAttributeNames()).isNotNull(); - assertThat(session.getAttributeNames()).isEmpty(); + assertThat(session.getAttributes()).isNotNull(); + assertThat(session.getAttributes()).isEmpty(); } @Test(expected = IllegalArgumentException.class) @@ -1228,33 +1250,34 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Session mockSession = mockSession("2", expectedCreationTime.toEpochMilli(), expectedLastAccessTime.toEpochMilli(), MAX_INACTIVE_INTERVAL_IN_SECONDS); - Set expectedAttributedNames = asSet("attrOne", "attrTwo"); + Set expectedAttributedNames = asSet("attributeOne", "attributeTwo"); when(mockSession.getAttributeNames()).thenReturn(expectedAttributedNames); - when(mockSession.getAttribute(eq("attrOne"))).thenReturn("testOne"); - when(mockSession.getAttribute(eq("attrTwo"))).thenReturn("testTwo"); + when(mockSession.getAttribute(eq("attributeOne"))).thenReturn("testOne"); + when(mockSession.getAttribute(eq("attributeTwo"))).thenReturn("testTwo"); GemFireSession gemfireSession = new GemFireSession(mockSession); assertThat(gemfireSession.getId()).isEqualTo("2"); assertThat(gemfireSession.getCreationTime()).isEqualTo(expectedCreationTime); + assertThat(gemfireSession.hasDelta()).isTrue(); assertThat(gemfireSession.getLastAccessedTime()).isEqualTo(expectedLastAccessTime); assertThat(gemfireSession.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveInterval); assertThat(gemfireSession.getAttributeNames()).isEqualTo(expectedAttributedNames); - assertThat(gemfireSession.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(gemfireSession.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(gemfireSession.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(gemfireSession.getAttribute("attributeTwo")).isEqualTo("testTwo"); verify(mockSession, times(1)).getId(); verify(mockSession, times(1)).getCreationTime(); verify(mockSession, times(1)).getLastAccessedTime(); verify(mockSession, times(1)).getMaxInactiveInterval(); verify(mockSession, times(1)).getAttributeNames(); - verify(mockSession, times(1)).getAttribute(eq("attrOne")); - verify(mockSession, times(1)).getAttribute(eq("attrTwo")); + verify(mockSession, times(1)).getAttribute(eq("attributeOne")); + verify(mockSession, times(1)).getAttribute(eq("attributeTwo")); } @Test(expected = IllegalArgumentException.class) - public void constructGemFireSessionWithNullSession() { + public void constructGemFireSessionWithNull() { try { new GemFireSession((Session) null); @@ -1269,9 +1292,26 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void createNewGemFireSession() { + public void createNewGemFireSessionWithDefaultMaxInactiveInterval() { - Instant beforeOrAtCreationTime = Instant.now(); + Instant testCreationTime = Instant.now(); + + GemFireSession session = GemFireSession.create(); + + assertThat(session).isNotNull(); + assertThat(session.getId()).isNotNull(); + assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.hasDelta()).isTrue(); + assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(GemFireSession.DEFAULT_MAX_INACTIVE_INTERVAL); + assertThat(session.getAttributes()).isNotNull(); + assertThat(session.getAttributes()).isEmpty(); + } + + @Test + public void createNewGemFireSessionWithProvidedMaxInactiveInterval() { + + Instant testCreationTime = Instant.now(); Duration maxInactiveInterval = Duration.ofSeconds(120L); @@ -1279,31 +1319,16 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session).isNotNull(); assertThat(session.getId()).isNotNull(); - assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.hasDelta()).isTrue(); assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactiveInterval); - assertThat(session.getAttributeNames()).isNotNull(); - assertThat(session.getAttributeNames()).isEmpty(); - } - - @Test - public void createNewGemFireSessionWithDefaultMaxInactiveInterval() { - - Instant beforeOrAtCreationTime = Instant.now(); - - GemFireSession session = GemFireSession.create(); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isNotNull(); - assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); - assertThat(session.getMaxInactiveInterval()).isEqualTo(GemFireSession.DEFAULT_MAX_INACTIVE_INTERVAL); - assertThat(session.getAttributeNames()).isNotNull(); - assertThat(session.getAttributeNames()).isEmpty(); + assertThat(session.getAttributes()).isNotNull(); + assertThat(session.getAttributes()).isEmpty(); } @Test(expected = IllegalArgumentException.class) - public void copyNullSessionThrowsIllegalArgumentException() { + public void copyNullThrowsException() { try { GemFireSession.copy(null); @@ -1332,11 +1357,19 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(sessionCopy).isNotNull(); assertThat(sessionCopy).isNotInstanceOf(DeltaCapableGemFireSession.class); assertThat(sessionCopy.getId()).isEqualTo(mockSession.getId()); + assertThat(sessionCopy.getCreationTime()).isEqualTo(mockSession.getCreationTime()); + assertThat(sessionCopy.hasDelta()).isTrue(); + assertThat(sessionCopy.getLastAccessedTime()).isEqualTo(mockSession.getLastAccessedTime()); + assertThat(sessionCopy.getMaxInactiveInterval()).isEqualTo(mockSession.getMaxInactiveInterval()); assertThat(sessionCopy.getAttributeNames()).containsExactly("attributeOne"); assertThat(sessionCopy.getAttribute("attributeOne")).isEqualTo("test"); - assertThat(sessionCopy.getCreationTime()).isEqualTo(mockSession.getCreationTime()); - assertThat(sessionCopy.getLastAccessedTime()).isEqualTo(mockSession.getCreationTime()); - assertThat(sessionCopy.getMaxInactiveInterval()).isEqualTo(mockSession.getMaxInactiveInterval()); + + verify(mockSession, times(2)).getId(); + verify(mockSession, times(2)).getCreationTime(); + verify(mockSession, times(2)).getLastAccessedTime(); + verify(mockSession, times(2)).getMaxInactiveInterval(); + verify(mockSession, times(1)).getAttributeNames(); + verify(mockSession, times(1)).getAttribute(eq("attributeOne")); } @Test @@ -1355,46 +1388,23 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(sessionCopy).isInstanceOf(DeltaCapableGemFireSession.class); assertThat(sessionCopy.getId()).isEqualTo(mockSession.getId()); + assertThat(sessionCopy.getCreationTime()).isEqualTo(mockSession.getCreationTime()); + assertThat(sessionCopy.hasDelta()).isTrue(); + assertThat(sessionCopy.getLastAccessedTime()).isEqualTo(mockSession.getLastAccessedTime()); + assertThat(sessionCopy.getMaxInactiveInterval()).isEqualTo(mockSession.getMaxInactiveInterval()); assertThat(sessionCopy.getAttributeNames()).containsExactly("attributeOne"); assertThat(sessionCopy.getAttribute("attributeOne")).isEqualTo("test"); - assertThat(sessionCopy.getCreationTime()).isEqualTo(mockSession.getCreationTime()); - assertThat(sessionCopy.getLastAccessedTime()).isEqualTo(mockSession.getCreationTime()); - assertThat(sessionCopy.getMaxInactiveInterval()).isEqualTo(mockSession.getMaxInactiveInterval()); + + verify(mockSession, times(2)).getId(); + verify(mockSession, times(2)).getCreationTime(); + verify(mockSession, times(2)).getLastAccessedTime(); + verify(mockSession, times(2)).getMaxInactiveInterval(); + verify(mockSession, times(1)).getAttributeNames(); + verify(mockSession, times(1)).getAttribute(eq("attributeOne")); } @Test - public void copyCommittedCopiesAndThenCommitsGemFireSession() { - - GemFireSession session = spy(GemFireSession.create()); - GemFireSession sessionCopy = GemFireSession.copyCommitted(session); - - assertThat(sessionCopy).isNotNull(); - assertThat(sessionCopy).isNotSameAs(session); - assertThat(sessionCopy).isEqualTo(session); - - verify(session, times(1)).getAttributeNames(); - verify(session, times(1)).commit(); - } - - @Test - public void copyCommittedOnlyCopiesStandardSession() { - - Session mockSession = mockSession(); - - GemFireSession sessionCopy = GemFireSession.copyCommitted(mockSession); - - assertThat(sessionCopy).isNotNull(); - assertThat(sessionCopy).isNotSameAs(mockSession); - assertThat(sessionCopy.getId()).isEqualTo(mockSession.getId()); - } - - @Test - public void copyCommittedIsAtomic() throws Throwable { - TestFramework.runOnce(new ThreadSafeCopyCommittedSessionTestCase()); - } - - @Test - public void fromExistingSession() { + public void fromExistingSessionCopiesSession() { Instant expectedCreationTime = Instant.ofEpochMilli(1L); Instant expectedLastAccessedTime = Instant.ofEpochMilli(2L); @@ -1411,10 +1421,11 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(gemfireSession).isNotNull(); assertThat(gemfireSession.getId()).isEqualTo("4"); assertThat(gemfireSession.getCreationTime()).isEqualTo(expectedCreationTime); + assertThat(gemfireSession.hasDelta()).isTrue(); assertThat(gemfireSession.getLastAccessedTime()).isEqualTo(expectedLastAccessedTime); assertThat(gemfireSession.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveInterval); - assertThat(gemfireSession.getAttributeNames()).isNotNull(); - assertThat(gemfireSession.getAttributeNames().isEmpty()).isTrue(); + assertThat(gemfireSession.getAttributes()).isNotNull(); + assertThat(gemfireSession.getAttributes()).isEmpty(); verify(mockSession, times(1)).getId(); verify(mockSession, times(1)).getCreationTime(); @@ -1442,33 +1453,33 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session).isNotNull(); assertThat(session.getAttributeNames()).isEmpty(); - session.setAttribute("attrOne", "testOne"); + session.setAttribute("attributeOne", "testOne"); - assertThat(session.getAttributeNames()).isEqualTo(asSet("attrOne")); - assertThat(session.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(session.getAttribute("attrTwo")).isNull(); + assertThat(session.getAttributeNames()).containsOnly("attributeOne"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(session.getAttribute("attributeTwo")).isNull(); - session.setAttribute("attrTwo", "testTwo"); + session.setAttribute("attributeTwo", "testTwo"); - assertThat(session.getAttributeNames()).isEqualTo(asSet("attrOne", "attrTwo")); - assertThat(session.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(session.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(session.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(session.getAttribute("attributeTwo")).isEqualTo("testTwo"); - session.setAttribute("attrTwo", null); + session.setAttribute("attributeTwo", null); - assertThat(session.getAttributeNames()).isEqualTo(asSet("attrOne")); - assertThat(session.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(session.getAttribute("attrTwo")).isNull(); + assertThat(session.getAttributeNames()).containsOnly("attributeOne"); + assertThat(session.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(session.getAttribute("attributeTwo")).isNull(); - session.removeAttribute("attrOne"); + session.removeAttribute("attributeOne"); - assertThat(session.getAttribute("attrOne")).isNull(); - assertThat(session.getAttribute("attrTwo")).isNull(); - assertThat(session.getAttributeNames().isEmpty()).isTrue(); + assertThat(session.getAttributeNames()).isEmpty(); + assertThat(session.getAttribute("attributeOne")).isNull(); + assertThat(session.getAttribute("attributeTwo")).isNull(); } @Test - public void isExpiredReturnsFalseWhenMaxInactiveIntervalIsNegative() { + public void isExpiredWhenMaxInactiveIntervalIsNegativeReturnsFalse() { Duration expectedMaxInactiveIntervalInSeconds = Duration.ofSeconds(-1); @@ -1480,7 +1491,20 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void isExpiredReturnsFalseWhenMaxInactiveIntervalIsZero() { + public void isExpiredWhenMaxInactiveIntervalIsNullReturnsFalse() { + + GemFireSession session = GemFireSession.create(null); + + assertThat(session).isNotNull(); + + session.setMaxInactiveInterval(null); + + assertThat(session.getMaxInactiveInterval()).isEqualTo(GemFireSession.DEFAULT_MAX_INACTIVE_INTERVAL); + assertThat(session.isExpired()).isFalse(); + } + + @Test + public void isExpiredWhenMaxInactiveIntervalIsZeroReturnsFalse() { Duration expectedMaxInactiveIntervalInSeconds = Duration.ZERO; @@ -1492,7 +1516,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void isExpiredReturnsFalseWhenSessionIsActive() { + public void isExpiredWhenSessionIsActiveReturnsFalse() { long expectedMaxInactiveIntervalInSeconds = TimeUnit.HOURS.toSeconds(2); @@ -1511,7 +1535,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void isExpiredReturnsTrueWhenSessionIsInactive() { + public void isExpiredWhenSessionIsInactiveReturnsTrue() { int expectedMaxInactiveIntervalInSeconds = 60; @@ -1532,16 +1556,16 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void setAndGetPrincipalName() { - GemFireSession session = GemFireSession.create(Duration.ZERO); + GemFireSession session = GemFireSession.create(); assertThat(session).isNotNull(); assertThat(session.getPrincipalName()).isNull(); - session.setPrincipalName("jblum"); + session.setPrincipalName("jxblum"); - assertThat(session.getPrincipalName()).isEqualTo("jblum"); + assertThat(session.getPrincipalName()).isEqualTo("jxblum"); assertThat(session.getAttributeNames()).isEqualTo(asSet(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME)); - assertThat(session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME)).isEqualTo("jblum"); + assertThat(session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME)).isEqualTo("jxblum"); session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "rwinch"); @@ -1554,67 +1578,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.getPrincipalName()).isNull(); } - @Test - public void hasDeltaReturnsFalseWhenNoSessionChanges() { - assertThat(new AbstractGemFireOperationsSessionRepository.GemFireSession().hasDelta()).isFalse(); - } - - @Test - public void hasDeltaReturnsTrueWhenSessionAttributesChange() { - - GemFireSession session = new DeltaCapableGemFireSession(); - - assertThat(session.hasDelta()).isFalse(); - - session.setAttribute("attrOne", "test"); - - assertThat(session.hasDelta()).isTrue(); - } - - @Test - public void hasDeltaReturnsTrueWhenSessionLastAccessedTimeIsUpdated() { - - Instant expectedLastAccessTime = Instant.ofEpochMilli(1L); - - AbstractGemFireOperationsSessionRepository.GemFireSession session = - new AbstractGemFireOperationsSessionRepository.GemFireSession(); - - assertThat(session.getLastAccessedTime()).isNotEqualTo(expectedLastAccessTime); - assertThat(session.hasDelta()).isFalse(); - - session.setLastAccessedTime(expectedLastAccessTime); - - assertThat(session.getLastAccessedTime()).isEqualTo(expectedLastAccessTime); - assertThat(session.hasDelta()).isTrue(); - - session.setLastAccessedTime(expectedLastAccessTime); - - assertThat(session.getLastAccessedTime()).isEqualTo(expectedLastAccessTime); - assertThat(session.hasDelta()).isTrue(); - } - - @Test - public void hasDeltaReturnsTrueWhenSessionMaxInactiveIntervalInSecondsIsUpdated() { - - Duration expectedMaxInactiveIntervalInSeconds = Duration.ofSeconds(300L); - - AbstractGemFireOperationsSessionRepository.GemFireSession session = - new AbstractGemFireOperationsSessionRepository.GemFireSession(); - - assertThat(session.getMaxInactiveInterval()).isNotEqualTo(expectedMaxInactiveIntervalInSeconds); - assertThat(session.hasDelta()).isFalse(); - - session.setMaxInactiveInterval(expectedMaxInactiveIntervalInSeconds); - - assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveIntervalInSeconds); - assertThat(session.hasDelta()).isTrue(); - - session.setMaxInactiveInterval(expectedMaxInactiveIntervalInSeconds); - - assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveIntervalInSeconds); - assertThat(session.hasDelta()).isTrue(); - } - @Test public void sessionToDelta() throws Exception { @@ -1625,18 +1588,18 @@ public class AbstractGemFireOperationsSessionRepositoryTests { session.setLastAccessedTime(Instant.ofEpochMilli(1L)); session.setMaxInactiveInterval(Duration.ofSeconds(300L)); - session.setAttribute("attrOne", "test"); + session.setAttribute("attributeOne", "test"); assertThat(session.hasDelta()).isTrue(); session.toDelta(mockDataOutput); - assertThat(session.hasDelta()).isFalse(); + assertThat(session.hasDelta()).isTrue(); verify(mockDataOutput, times(1)).writeLong(eq(1L)); verify(mockDataOutput, times(1)).writeLong(eq(300L)); verify(mockDataOutput, times(1)).writeInt(eq(1)); - verify(mockDataOutput, times(1)).writeUTF(eq("attrOne")); + verify(mockDataOutput, times(1)).writeUTF(eq("attributeOne")); } @Test @@ -1648,14 +1611,16 @@ public class AbstractGemFireOperationsSessionRepositoryTests { when(mockDataInput.readLong()).thenReturn(1L).thenReturn(600L); when(mockDataInput.readInt()).thenReturn(0); - @SuppressWarnings("serial") DeltaCapableGemFireSession session = new DeltaCapableGemFireSession(); + Instant creationTime = session.getCreationTime(); + session.fromDelta(mockDataInput); - assertThat(session.hasDelta()).isFalse(); assertThat(session.getId()).isEqualTo("1"); + assertThat(session.getCreationTime()).isEqualTo(creationTime); assertThat(session.getLastAccessedTime()).isEqualTo(Instant.ofEpochMilli(1L)); + assertThat(session.hasDelta()).isTrue(); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(600L)); assertThat(session.getAttributeNames().isEmpty()).isTrue(); @@ -1665,45 +1630,50 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void isDirtyReturnsFalseWithExistingSession() { + public void sessionHasDeltaWithExistingSessionReturnsTrue() { - GemFireSession session = GemFireSession.from(this.mockSession); + GemFireSession session = GemFireSession.from(mockSession()); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isTrue(); } @Test - public void isDirtyReturnsTrueWithNewGemFireSession() { + public void sessionHasDeltaWithNewSessionReturnsTrue() { GemFireSession session = GemFireSession.create(); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); } @Test - public void isDirtyReturnsTrueWhenSessionAttributeIsAdded() { + public void sessionHasDeltaWhenSessionIsNotDirtyReturnsFalse() { + assertThat(newNonDirtyGemFireSession().hasDelta()).isFalse(); + } - GemFireSession session = GemFireSession.from(this.mockSession); + @Test + public void sessionHasDeltaWhenSessionAttributeIsAddedReturnsTrue() { + + GemFireSession session = newNonDirtyGemFireSession(); assertThat(session).isNotNull(); assertThat(session.getAttributeNames()).isEmpty(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); session.setAttribute("attributeOne", "one"); assertThat(session.getAttributeNames()).containsExactly("attributeOne"); assertThat(session.getAttribute("attributeOne")).isEqualTo("one"); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionAttributeIsRemoved() { + public void sessionHasDeltaWhenSessionAttributeIsRemovedReturnsTrue() { GemFireSession session = GemFireSession.create(); @@ -1714,21 +1684,21 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.getAttributeNames()).containsExactly("attributeOne"); assertThat(session.getAttribute("attributeOne")).isEqualTo("one"); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); session.removeAttribute("attributeOne"); assertThat(session.getAttributeNames()).isEmpty(); assertThat(session.getAttribute("attributeOne")).isNull(); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionAttributeIsUpdated() { + public void sessionHasDeltaWhenSessionAttributeIsUpdatedReturnsTrue() { GemFireSession session = GemFireSession.create(); @@ -1739,46 +1709,46 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.getAttributeNames()).containsExactly("attributeOne"); assertThat(session.getAttribute("attributeOne")).isEqualTo("one"); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); session.setAttribute("attributeOne", "two"); assertThat(session.getAttributeNames()).containsExactly("attributeOne"); assertThat(session.getAttribute("attributeOne")).isEqualTo("two"); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionIdChanges() { + public void sessionHasDeltaWhenSessionIdChangesReturnsTrue() { - GemFireSession session = GemFireSession.from(mockSession()); + GemFireSession session = newNonDirtyGemFireSession(); assertThat(session).isNotNull(); assertThat(session.getId()).isNotEmpty(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); String currentSessionId = session.getId(); assertThat(currentSessionId).isNotEmpty(); assertThat(session.changeSessionId()).isNotEqualTo(currentSessionId); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionLastAccessedTimeChanges() { + public void sessionHasDeltaWhenSessionLastAccessedTimeChangesReturnsTrue() { - GemFireSession session = GemFireSession.from(mockSession()); + GemFireSession session = newNonDirtyGemFireSession(); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); Instant lastAccessedTime = session.getLastAccessedTime(); @@ -1787,20 +1757,20 @@ public class AbstractGemFireOperationsSessionRepositoryTests { session.setLastAccessedTime(lastAccessedTime.plus(Duration.ofSeconds(5))); assertThat(session.getLastAccessedTime()).isAfter(lastAccessedTime); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionMaxInactiveIntervalChanges() { + public void sessionHasDeltaWhenSessionMaxInactiveIntervalChangesReturnsTrue() { - GemFireSession session = GemFireSession.from(mockSession()); + GemFireSession session = newNonDirtyGemFireSession(); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); Duration maxInactiveInterval = session.getMaxInactiveInterval(); @@ -1809,40 +1779,61 @@ public class AbstractGemFireOperationsSessionRepositoryTests { session.setMaxInactiveInterval(maxInactiveInterval.plus(Duration.ofSeconds(5))); assertThat(session.getMaxInactiveInterval()).isGreaterThan(maxInactiveInterval); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.commit(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.hasDelta()).isFalse(); } @Test - public void isDirtyReturnsTrueWhenSessionIsDirtyAndAttributesAreNotModifiedOnSubsequentUpdate() { + public void sessionHasDeltaWhenSessionPrincipalNameChangesReturnsTrue() { - GemFireSession session = GemFireSession.from(mockSession()); + GemFireSession session = newNonDirtyGemFireSession(); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isFalse(); + assertThat(session.getPrincipalName()).isNull(); + assertThat(session.hasDelta()).isFalse(); + + session.setPrincipalName("jxblum"); + + assertThat(session.getPrincipalName()).isEqualTo("jxblum"); + assertThat(session.hasDelta()).isTrue(); + + session.commit(); + + assertThat(session.hasDelta()).isFalse(); + } + + @Test + public void sessionHasDeltaWhenSessionIsDirtyAndAttributesAreNotModifiedOnSubsequentUpdatesReturnsTrue() { + + GemFireSession session = newNonDirtyGemFireSession(); + + assertThat(session).isNotNull(); + assertThat(session.hasDelta()).isFalse(); String previousSessionId = session.getId(); Instant newLastAccessedTime = session.getLastAccessedTime().plusSeconds(5L); - Duration newMaxLastInactiveInterval = session.getMaxInactiveInterval().plusSeconds(5L); + Duration newMaxInactiveInterval = session.getMaxInactiveInterval().plusSeconds(5L); assertThat(session.changeSessionId()).isNotEqualTo(previousSessionId); session.setAttribute("attributeOne", "testOne"); session.setLastAccessedTime(newLastAccessedTime); - session.setMaxInactiveInterval(newMaxLastInactiveInterval); + session.setMaxInactiveInterval(newMaxInactiveInterval); + session.setPrincipalName("TestPrincipal"); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); session.setAttribute("attributeOne", "testOne"); session.setLastAccessedTime(newLastAccessedTime); - session.setMaxInactiveInterval(newMaxLastInactiveInterval); + session.setMaxInactiveInterval(newMaxInactiveInterval); + session.setPrincipalName("TestPrincipal"); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); } @Test @@ -1850,8 +1841,9 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Instant twoHoursAgo = Instant.now().minusMillis(TimeUnit.HOURS.toMillis(2)); - GemFireSession sessionOne = - new GemFireSession<>(mockSession("1", twoHoursAgo.toEpochMilli(), MAX_INACTIVE_INTERVAL_IN_SECONDS)); + Session mockSession = mockSession("1", twoHoursAgo.toEpochMilli(), MAX_INACTIVE_INTERVAL_IN_SECONDS); + + GemFireSession sessionOne = new GemFireSession<>(mockSession); GemFireSession sessionTwo = new GemFireSession<>("2"); @@ -1863,13 +1855,18 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test + @SuppressWarnings("all") public void sessionEqualsDifferentSessionBasedOnId() { GemFireSession sessionOne = new GemFireSession("1"); sessionOne.setLastAccessedTime(Instant.ofEpochSecond(12345L)); sessionOne.setMaxInactiveInterval(Duration.ofSeconds(120L)); - sessionOne.setPrincipalName("jblum"); + sessionOne.setPrincipalName("jxblum"); + + long timestamp = System.currentTimeMillis(); + + while (System.currentTimeMillis() == timestamp); GemFireSession sessionTwo = new GemFireSession("1"); @@ -1877,15 +1874,39 @@ public class AbstractGemFireOperationsSessionRepositoryTests { sessionTwo.setMaxInactiveInterval(Duration.ofSeconds(300L)); sessionTwo.setPrincipalName("rwinch"); - assertThat(sessionOne.getId().equals(sessionTwo.getId())).isTrue(); - assertThat(sessionOne.getLastAccessedTime() == sessionTwo.getLastAccessedTime()).isFalse(); - assertThat(sessionOne.getMaxInactiveInterval() == sessionTwo.getMaxInactiveInterval()).isFalse(); - assertThat(sessionOne.getPrincipalName().equals(sessionTwo.getPrincipalName())).isFalse(); + assertThat(sessionTwo.getId().equals(sessionOne.getId())).isTrue(); + assertThat(sessionTwo.getCreationTime()).isNotEqualTo(sessionOne.getCreationTime()); + assertThat(sessionTwo.getLastAccessedTime()).isNotEqualTo(sessionOne.getLastAccessedTime()); + assertThat(sessionTwo.getMaxInactiveInterval()).isNotEqualTo(sessionOne.getMaxInactiveInterval()); + assertThat(sessionTwo.getPrincipalName()).isNotEqualTo(sessionOne.getPrincipalName()); assertThat(sessionOne.equals(sessionTwo)).isTrue(); + assertThat(sessionTwo.equals(sessionOne)).isTrue(); } @Test - public void sessionHashCodeIsNotEqualToStringIdHashCode() { + public void sessionIsNotEqualToDifferentSessionBasedOnId() { + + GemFireSession sessionOne = new GemFireSession("1"); + + sessionOne.setLastAccessedTime(Instant.ofEpochSecond(12345L)); + sessionOne.setMaxInactiveInterval(Duration.ofSeconds(120L)); + sessionOne.setPrincipalName("jxblum"); + + GemFireSession sessionTwo = new GemFireSession(sessionOne); + + sessionTwo.changeSessionId(); + + assertThat(sessionTwo.getId().equals(sessionOne.getId())).isFalse(); + assertThat(sessionTwo.getCreationTime()).isEqualTo(sessionOne.getCreationTime()); + assertThat(sessionTwo.getLastAccessedTime()).isEqualTo(sessionOne.getLastAccessedTime()); + assertThat(sessionTwo.getMaxInactiveInterval()).isEqualTo(sessionOne.getMaxInactiveInterval()); + assertThat(sessionTwo.getPrincipalName()).isEqualTo(sessionOne.getPrincipalName()); + assertThat(sessionOne.equals(sessionTwo)).isFalse(); + assertThat(sessionTwo.equals(sessionOne)).isFalse(); + } + + @Test + public void sessionHashCodeIsNotEqualToStringHashCode() { GemFireSession session = new GemFireSession("1"); @@ -1893,6 +1914,30 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.hashCode()).isNotEqualTo("1".hashCode()); } + @Test + public void sessionHashCodeWithEqualSessionsHaveSameHashCode() { + + GemFireSession sessionOne = new GemFireSession("1"); + GemFireSession sessionTwo = new GemFireSession(sessionOne); + + assertThat(sessionOne).isNotSameAs(sessionTwo); + assertThat(sessionOne).isEqualTo(sessionTwo); + assertThat(sessionOne.hashCode()).isEqualTo(sessionTwo.hashCode()); + } + + @Test + public void sessionHashCodeWithUnequalSessionsHaveDifferentHashCodes() { + + GemFireSession sessionOne = new GemFireSession("1"); + GemFireSession sessionTwo = new GemFireSession(sessionOne); + + sessionTwo.changeSessionId(); + + assertThat(sessionOne).isNotSameAs(sessionTwo); + assertThat(sessionOne).isNotEqualTo(sessionTwo); + assertThat(sessionOne.hashCode()).isNotEqualTo(sessionTwo.hashCode()); + } + @Test @SuppressWarnings("unchecked") public void sessionToStringContainsId() { @@ -1911,19 +1956,19 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Map source = new HashMap<>(); - source.put("attrOne", "testOne"); - source.put("attrTwo", "testTwo"); + source.put("attributeOne", "testOne"); + source.put("attributeTwo", "testTwo"); GemFireSessionAttributes target = new GemFireSessionAttributes(); - assertThat(target.getAttributeNames()).isEmpty(); + assertThat(target).isEmpty(); target.from(source); - assertThat(target.getAttributeNames().size()).isEqualTo(2); - assertThat(target.getAttributeNames()).containsOnly("attrOne", "attrTwo"); - assertThat(target.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(target.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(target).hasSize(2); + assertThat(target.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(target.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(target.getAttribute("attributeTwo")).isEqualTo("testTwo"); } @Test @@ -1931,24 +1976,24 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Session mockSession = mock(Session.class); - when(mockSession.getAttributeNames()).thenReturn(asSet("attrOne", "attrTwo")); - when(mockSession.getAttribute(eq("attrOne"))).thenReturn("testOne"); - when(mockSession.getAttribute(eq("attrTwo"))).thenReturn("testTwo"); + when(mockSession.getAttributeNames()).thenReturn(asSet("attributeOne", "attributeTwo")); + when(mockSession.getAttribute(eq("attributeOne"))).thenReturn("testOne"); + when(mockSession.getAttribute(eq("attributeTwo"))).thenReturn("testTwo"); GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes).isEmpty(); sessionAttributes.from(mockSession); - assertThat(sessionAttributes.getAttributeNames().size()).isEqualTo(2); - assertThat(sessionAttributes.getAttributeNames()).containsOnly("attrOne", "attrTwo"); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(sessionAttributes).hasSize(2); + assertThat(sessionAttributes.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("testTwo"); verify(mockSession, times(1)).getAttributeNames(); - verify(mockSession, times(1)).getAttribute(eq("attrOne")); - verify(mockSession, times(1)).getAttribute(eq("attrTwo")); + verify(mockSession, times(1)).getAttribute(eq("attributeOne")); + verify(mockSession, times(1)).getAttribute(eq("attributeTwo")); } @Test @@ -1956,169 +2001,139 @@ public class AbstractGemFireOperationsSessionRepositoryTests { GemFireSessionAttributes source = new GemFireSessionAttributes(); - source.setAttribute("attrOne", "testOne"); - source.setAttribute("attrTwo", "testTwo"); + source.setAttribute("attributeOne", "testOne"); + source.setAttribute("attributeTwo", "testTwo"); GemFireSessionAttributes target = new GemFireSessionAttributes(); - assertThat(target.getAttributeNames()).isEmpty(); + assertThat(target).isEmpty(); target.from(source); - assertThat(target.getAttributeNames().size()).isEqualTo(2); - assertThat(target.getAttributeNames()).containsOnly("attrOne", "attrTwo"); - assertThat(target.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(target.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(target).hasSize(2); + assertThat(target.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(target.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(target.getAttribute("attributeTwo")).isEqualTo("testTwo"); } @Test - public void sessionAttributesIsDirtyReturnsTrueOnSetWhenAdd() { + public void sessionAttributesToDelta() throws Exception { - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + AtomicInteger count = new AtomicInteger(0); - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.isDirty()).isFalse(); + DataOutput mockDataOutput = mock(DataOutput.class); + + @SuppressWarnings("serial") + DeltaCapableGemFireSessionAttributes sessionAttributes = spy(new DeltaCapableGemFireSessionAttributes()); + + doAnswer(invocation -> { + + assertThat(Arrays.asList("testOne", "testTwo", "testThree").get(count.getAndIncrement())) + .isEqualTo(invocation.getArgument(0)); + + assertThat(invocation.getArgument(1)).isSameAs(mockDataOutput); + + return null; + + }).when(sessionAttributes).writeObject(any(), isA(DataOutput.class)); sessionAttributes.setAttribute("attributeOne", "testOne"); + sessionAttributes.setAttribute("attributeTwo", "testTwo"); - // Set attribute to the same value again to make sure it does not clear the dirty bit + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.toDelta(mockDataOutput); + + assertThat(sessionAttributes.hasDelta()).isTrue(); + + verify(mockDataOutput, times(1)).writeInt(eq(2)); + verify(mockDataOutput, times(1)).writeUTF("attributeOne"); + verify(mockDataOutput, times(1)).writeUTF("attributeTwo"); + reset(mockDataOutput); + + sessionAttributes.commit(); sessionAttributes.setAttribute("attributeOne", "testOne"); - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isTrue(); + assertThat(sessionAttributes.hasDelta()).isFalse(); - sessionAttributes.commit(); + sessionAttributes.toDelta(mockDataOutput); - assertThat(sessionAttributes.isDirty()).isFalse(); + verify(mockDataOutput, times(1)).writeInt(eq(0)); + verify(mockDataOutput, never()).writeUTF(any(String.class)); + reset(mockDataOutput); + + sessionAttributes.setAttribute("attributeTwo", "testThree"); + + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.toDelta(mockDataOutput); + + assertThat(sessionAttributes.hasDelta()).isTrue(); + + verify(mockDataOutput, times(1)).writeInt(eq(1)); + verify(mockDataOutput, times(1)).writeUTF(eq("attributeTwo")); } @Test - public void sessionAttributesIsDirtyReturnsTrueOnSetWhenModified() { + public void sessionAttributesFromDelta() throws Exception { - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + DataInput mockDataInput = mock(DataInput.class); - sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); - sessionAttributes.commit(); + when(mockDataInput.readInt()).thenReturn(2); + when(mockDataInput.readUTF()).thenReturn("attributeOne").thenReturn("attributeTwo"); - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + DeltaCapableGemFireSessionAttributes sessionAttributes = spy(new DeltaCapableGemFireSessionAttributes()); + + AtomicInteger count = new AtomicInteger(0); + + doAnswer(invocation -> { + + assertThat(invocation.getArgument(0)).isSameAs(mockDataInput); + + return Arrays.asList("testOne", "testTwo", "testThree").get(count.getAndIncrement()); + + }).when(sessionAttributes).readObject(any(DataInput.class)); + + sessionAttributes.setAttribute("attributeOne", "one"); + sessionAttributes.setAttribute("attributeTwo", "two"); + + assertThat(sessionAttributes.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("one"); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("two"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.fromDelta(mockDataInput); + + assertThat(sessionAttributes.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("testTwo"); + assertThat(sessionAttributes.hasDelta()).isFalse(); - sessionAttributes.setAttribute("attributeOne", "testTwo"); + verify(mockDataInput, times(1)).readInt(); + verify(mockDataInput, times(2)).readUTF(); - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testTwo"); - assertThat(sessionAttributes.isDirty()).isTrue(); + reset(mockDataInput); - sessionAttributes.commit(); + when(mockDataInput.readInt()).thenReturn(1); + when(mockDataInput.readUTF()).thenReturn("attributeTwo"); - assertThat(sessionAttributes.isDirty()).isFalse(); - } + sessionAttributes.setAttribute("attributeOne", "one"); + sessionAttributes.setAttribute("attributeTwo", "two"); - @Test - public void sessionAttributesIsDirtyReturnsFalseOnSetWhenNotModified() { + assertThat(sessionAttributes.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("one"); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("two"); + assertThat(sessionAttributes.hasDelta()).isTrue(); - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + sessionAttributes.fromDelta(mockDataInput); - sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); - sessionAttributes.commit(); + assertThat(sessionAttributes.getAttributeNames()).containsOnly("attributeOne", "attributeTwo"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("one"); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("testThree"); + assertThat(sessionAttributes.hasDelta()).isTrue(); - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.setAttribute("attributeOne", "testOne"); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.commit(); - - assertThat(sessionAttributes.isDirty()).isFalse(); - } - - @Test - public void sessionAttributesIsDirtyReturnsTrueOnSetWhenRemoved() { - - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - - sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); - sessionAttributes.commit(); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.setAttribute("attributeOne", null); - - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.getAttribute("attributeOne")).isNull(); - assertThat(sessionAttributes.isDirty()).isTrue(); - - sessionAttributes.commit(); - - assertThat(sessionAttributes.isDirty()).isFalse(); - } - - @Test - public void sessionAttributesIsDirtyReturnsTrueOnRemove() { - - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - - sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); - sessionAttributes.commit(); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.removeAttribute("attributeOne"); - - // Remove attribute again to make sure it does not clear the dirty bit - sessionAttributes.removeAttribute("attributeOne"); - - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.getAttribute("attributeOne")).isNull(); - assertThat(sessionAttributes.isDirty()).isTrue(); - - sessionAttributes.commit(); - - assertThat(sessionAttributes.isDirty()).isFalse(); - } - - @Test - public void sessionAttributesIsDirtyReturnsFalseOnRemoveForNonExistingAttribute() { - - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - - sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); - sessionAttributes.commit(); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.removeAttribute("nonExistingAttribute"); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); - assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.isDirty()).isFalse(); - } - - @Test - public void sessionAttributesIsDirtyReturnsFalseOnRemoveForNullAttribute() { - - GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.isDirty()).isFalse(); - - sessionAttributes.removeAttribute(null); - - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.isDirty()).isFalse(); + verify(mockDataInput, times(1)).readInt(); + verify(mockDataInput, times(1)).readUTF(); } @Test @@ -2133,131 +2148,182 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(sessionAttributes.hasDelta()).isFalse(); - sessionAttributes.setAttribute("attrOne", "testOne"); + sessionAttributes.setAttribute("attributeOne", "testOne"); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("testOne"); + assertThat(sessionAttributes).hasSize(1); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.setAttribute("attributeOne", "testOne"); + + assertThat(sessionAttributes).hasSize(1); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.commit(); + + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.setAttribute("attributeTwo", "testTwo"); + + assertThat(sessionAttributes).hasSize(2); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.getAttribute("attributeTwo")).isEqualTo("testTwo"); assertThat(sessionAttributes.hasDelta()).isTrue(); } @Test - public void sessionAttributesToDelta() throws Exception { + public void sessionAttributesHasDeltaWhenSetAddsAttributeReturnsTrue() { - DataOutput mockDataOutput = mock(DataOutput.class); - - @SuppressWarnings("serial") - DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes() { - - private int count = 0; - - @Override - protected void writeObject(Object obj, DataOutput out) throws IOException { - assertThat(Arrays.asList("testOne", "testTwo", "testThree").get(count++)).isEqualTo(String.valueOf(obj)); - assertThat(out).isSameAs(mockDataOutput); - } - }; - - sessionAttributes.setAttribute("attrOne", "testOne"); - sessionAttributes.setAttribute("attrTwo", "testTwo"); - - assertThat(sessionAttributes.hasDelta()).isTrue(); - - sessionAttributes.toDelta(mockDataOutput); + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); assertThat(sessionAttributes.hasDelta()).isFalse(); - verify(mockDataOutput, times(1)).writeInt(eq(2)); - verify(mockDataOutput, times(1)).writeUTF("attrOne"); - verify(mockDataOutput, times(1)).writeUTF("attrTwo"); - reset(mockDataOutput); + sessionAttributes.setAttribute("attributeOne", "testOne"); - sessionAttributes.setAttribute("attrOne", "testOne"); - - assertThat(sessionAttributes.hasDelta()).isFalse(); - - sessionAttributes.toDelta(mockDataOutput); - - verify(mockDataOutput, times(1)).writeInt(eq(0)); - verify(mockDataOutput, never()).writeUTF(any(String.class)); - reset(mockDataOutput); - - sessionAttributes.setAttribute("attrTwo", "testThree"); + // Set attribute to the same value again to make sure it does not clear the dirty bit + sessionAttributes.setAttribute("attributeOne", "testOne"); + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); assertThat(sessionAttributes.hasDelta()).isTrue(); - sessionAttributes.toDelta(mockDataOutput); + sessionAttributes.commit(); - verify(mockDataOutput, times(1)).writeInt(eq(1)); - verify(mockDataOutput, times(1)).writeUTF(eq("attrTwo")); + assertThat(sessionAttributes.hasDelta()).isFalse(); } @Test - public void sessionAttributesFromDelta() throws Exception { + public void sessionAttributesHasDeltaWhenSetDoesNotModifyAttributeReturnsFalse() { - DataInput mockDataInput = mock(DataInput.class); + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - when(mockDataInput.readInt()).thenReturn(2); - when(mockDataInput.readUTF()).thenReturn("attrOne").thenReturn("attrTwo"); + sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); + sessionAttributes.commit(); - @SuppressWarnings("serial") - DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes() { - - private int count = 0; - - @Override - @SuppressWarnings("unchecked") - protected T readObject(DataInput in) throws ClassNotFoundException, IOException { - assertThat(in).isSameAs(mockDataInput); - return (T) Arrays.asList("testOne", "testTwo", "testThree").get(count++); - } - }; - - sessionAttributes.setAttribute("attrOne", "one"); - sessionAttributes.setAttribute("attrTwo", "two"); - - assertThat(sessionAttributes.getAttributeNames().size()).isEqualTo(2); - assertThat(sessionAttributes.getAttributeNames().containsAll(asSet("attrOne", "attrTwo"))).isTrue(); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("one"); - assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("two"); - assertThat(sessionAttributes.hasDelta()).isTrue(); - - sessionAttributes.fromDelta(mockDataInput); - - assertThat(sessionAttributes.getAttributeNames().size()).isEqualTo(2); - assertThat(sessionAttributes.getAttributeNames().containsAll(asSet("attrOne", "attrTwo"))).isTrue(); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); assertThat(sessionAttributes.hasDelta()).isFalse(); - verify(mockDataInput, times(1)).readInt(); - verify(mockDataInput, times(2)).readUTF(); - reset(mockDataInput); + sessionAttributes.setAttribute("attributeOne", "testOne"); - when(mockDataInput.readInt()).thenReturn(1); - when(mockDataInput.readUTF()).thenReturn("attrTwo"); + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); - sessionAttributes.setAttribute("attrOne", "one"); - sessionAttributes.setAttribute("attrTwo", "two"); + sessionAttributes.commit(); - assertThat(sessionAttributes.getAttributeNames().size()).isEqualTo(2); - assertThat(sessionAttributes.getAttributeNames().containsAll(asSet("attrOne", "attrTwo"))).isTrue(); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("one"); - assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("two"); - assertThat(sessionAttributes.hasDelta()).isTrue(); - - sessionAttributes.fromDelta(mockDataInput); - - assertThat(sessionAttributes.getAttributeNames().size()).isEqualTo(2); - assertThat(sessionAttributes.getAttributeNames().containsAll(asSet("attrOne", "attrTwo"))).isTrue(); - assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("one"); - assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("testThree"); - assertThat(sessionAttributes.hasDelta()).isTrue(); - - verify(mockDataInput, times(1)).readInt(); - verify(mockDataInput, times(1)).readUTF(); + assertThat(sessionAttributes.hasDelta()).isFalse(); } @Test - public void sessionAttributesEntrySetIteratesAttributeNameValues() { + public void sessionAttributesHasDeltaWhenSetModifiesAttributeReturnsTrue() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); + sessionAttributes.commit(); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.setAttribute("attributeOne", "testTwo"); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testTwo"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.commit(); + + assertThat(sessionAttributes.hasDelta()).isFalse(); + } + + @Test + public void sessionAttributesHasDeltaWhenSetRemovesAttributeReturnsTrue() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); + sessionAttributes.commit(); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.setAttribute("attributeOne", null); + + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.getAttribute("attributeOne")).isNull(); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.commit(); + + assertThat(sessionAttributes.hasDelta()).isFalse(); + } + + @Test + public void sessionAttributesHasDeltaWhenRemovingExistingAttributeReturnsTrue() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); + sessionAttributes.commit(); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.removeAttribute("attributeOne"); + + // Remove attribute again to make sure it does not clear the dirty bit + sessionAttributes.removeAttribute("attributeOne"); + + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.getAttribute("attributeOne")).isNull(); + assertThat(sessionAttributes.hasDelta()).isTrue(); + + sessionAttributes.commit(); + + assertThat(sessionAttributes.hasDelta()).isFalse(); + } + + @Test + public void sessionAttributesHasDeltaWhenRemovingNonExistingAttributeReturnsFalse() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + sessionAttributes.from(Collections.singletonMap("attributeOne", "testOne")); + sessionAttributes.commit(); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.removeAttribute("nonExistingAttribute"); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); + assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + } + + @Test + public void sessionAttributesHasDeltaWhenRemovingNullReturnsFalse() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.hasDelta()).isFalse(); + + sessionAttributes.removeAttribute(null); + + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.hasDelta()).isFalse(); + } + + @Test + public void sessionAttributesEntrySetIteratesAttributeNamesAndValues() { GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); @@ -2311,63 +2377,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { TestFramework.runOnce(new ThreadSafeSessionTestCase()); } - @SuppressWarnings("unused") - protected static final class ThreadSafeCopyCommittedSessionTestCase extends MultithreadedTestCase { - - private GemFireSession session; - - private Instant originalLastAccessedTime; - - @Override - public void initialize() { - - super.initialize(); - - this.session = spy(GemFireSession.create()); - this.originalLastAccessedTime = this.session.getLastAccessedTime(); - - doAnswer(invocation -> { - waitForTick(2); - return "123"; - }).when(this.session).getId(); - } - - public void thread1() { - - Thread.currentThread().setName("User Session One"); - - assertTick(0); - assertThat(this.session.isDirty()).isTrue(); - - GemFireSession sessionCopy = GemFireSession.copyCommitted(this.session); - - assertTick(2); - assertThat(sessionCopy).isNotNull(); - assertThat(sessionCopy).isNotSameAs(this.session); - assertThat(sessionCopy.getId()).isEqualTo("123"); - assertThat(sessionCopy.getLastAccessedTime()).isEqualTo(this.originalLastAccessedTime); - - verify(this.session, times(1)).commit(); - } - - public void thread2() { - - Thread.currentThread().setName("User Session Two"); - - waitForTick(1); - assertTick(1); - - this.session.setLastAccessedTime(this.originalLastAccessedTime.plusSeconds(5L)); - - waitForTick(3); - assertTick(3); - - assertThat(this.session.getId()).isEqualTo("123"); - assertThat(this.session.getLastAccessedTime()).isEqualTo(this.originalLastAccessedTime.plusSeconds(5L)); - assertThat(this.session.isDirty()).isTrue(); - } - } - @SuppressWarnings("unused") protected static final class ThreadSafeSessionTestCase extends MultithreadedTestCase { @@ -2394,7 +2403,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { this.session.setLastAccessedTime(Instant.MIN); this.session.setMaxInactiveInterval(Duration.ofSeconds(60L)); - this.session.setPrincipalName("jblum"); + this.session.setPrincipalName("jxblum"); } public void thread1() { @@ -2408,9 +2417,9 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(this.session.getCreationTime()).isEqualTo(this.expectedCreationTime); assertThat(this.session.getLastAccessedTime()).isEqualTo(Instant.MIN); assertThat(this.session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(60L)); - assertThat(this.session.getPrincipalName()).isEqualTo("jblum"); + assertThat(this.session.getPrincipalName()).isEqualTo("jxblum"); assertThat(this.session.getAttributeNames()).hasSize(1); - assertThat(this.session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME)).isEqualTo("jblum"); + assertThat(this.session.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME)).isEqualTo("jxblum"); this.session.setAttribute("tennis", "ping"); this.session.setAttribute("junk", "test"); diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepositoryTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepositoryTests.java index bebd2e0..881b3c8 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepositoryTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/GemFireOperationsSessionRepositoryTests.java @@ -120,7 +120,6 @@ public class GemFireOperationsSessionRepositoryTests { this.sessionRepository.setApplicationEventPublisher(this.mockApplicationEventPublisher); this.sessionRepository.setMaxInactiveIntervalInSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS); this.sessionRepository.setUseDataSerialization(false); - this.sessionRepository.setUseEagerCommit(false); this.sessionRepository.afterPropertiesSet(); assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(this.mockApplicationEventPublisher); @@ -128,7 +127,6 @@ public class GemFireOperationsSessionRepositoryTests { assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(MAX_INACTIVE_INTERVAL_IN_SECONDS); assertThat(this.sessionRepository.getTemplate()).isSameAs(this.mockTemplate); assertThat(GemFireOperationsSessionRepository.isUsingDataSerialization()).isFalse(); - assertThat(this.sessionRepository.isUsingEagerCommit()).isFalse(); } private Session mockSession() { @@ -150,6 +148,15 @@ public class GemFireOperationsSessionRepositoryTests { return mockSession; } + private GemFireSession newNonDirtyGemFireSession() { + + GemFireSession session = GemFireSession.create(); + + session.commit();; + + return session; + } + @After public void tearDown() { @@ -158,9 +165,111 @@ public class GemFireOperationsSessionRepositoryTests { verify(this.mockTemplate, times(1)).getRegion(); } + @Test + public void createProperlyInitializedSession() { + + Instant beforeOrAtCreationTime = Instant.now(); + + Session session = this.sessionRepository.createSession(); + + assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class); + assertThat(session.getId()).isNotNull(); + assertThat(session.getAttributeNames()).isEmpty(); + assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(session.getLastAccessedTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS)); + } + + @Test + public void findByIdDeletesMatchingExpiredSessionReturnsNull() { + + String expectedSessionId = "1"; + + Session mockSession = mock(Session.class); + + given(mockSession.isExpired()).willReturn(true); + given(mockSession.getId()).willReturn(expectedSessionId); + given(this.mockTemplate.get(eq(expectedSessionId))).willReturn(mockSession); + given(this.mockTemplate.remove(eq(expectedSessionId))).willReturn(mockSession); + + willAnswer(invocation -> { + + ApplicationEvent applicationEvent = invocation.getArgument(0); + + assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class); + + AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + + assertThat(sessionEvent.getSession()).isSameAs(mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId); + assertThat(sessionEvent.getSource()) + .isSameAs(GemFireOperationsSessionRepositoryTests.this.sessionRepository); + + return null; + + }).given(this.mockApplicationEventPublisher).publishEvent(any(ApplicationEvent.class)); + + assertThat(this.sessionRepository.findById(expectedSessionId)).isNull(); + + verify(this.mockTemplate, times(1)).get(eq(expectedSessionId)); + verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId)); + verify(mockSession, times(1)).isExpired(); + verify(mockSession, times(2)).getId(); + verify(this.mockApplicationEventPublisher, times(1)) + .publishEvent(isA(SessionDeletedEvent.class)); + } + + @Test + public void findByIdReturnsMatchingNonExpiredSession() { + + String expectedId = "1"; + + Instant expectedCreationTime = Instant.now(); + Instant currentLastAccessedTime = expectedCreationTime.plusMillis(TimeUnit.MINUTES.toMillis(5)); + + Session mockSession = mock(Session.class); + + given(mockSession.isExpired()).willReturn(false); + given(mockSession.getId()).willReturn(expectedId); + given(mockSession.getCreationTime()).willReturn(expectedCreationTime); + given(mockSession.getLastAccessedTime()).willReturn(currentLastAccessedTime); + given(mockSession.getAttributeNames()).willReturn(Collections.singleton("attrOne")); + given(mockSession.getAttribute(eq("attrOne"))).willReturn("test"); + given(this.mockTemplate.get(eq(expectedId))).willReturn(mockSession); + + Session actualSession = this.sessionRepository.findById(expectedId); + + assertThat(actualSession).isNotNull(); + assertThat(actualSession).isNotSameAs(mockSession); + assertThat(actualSession.getId()).isEqualTo(expectedId); + assertThat(actualSession.getCreationTime()).isEqualTo(expectedCreationTime); + assertThat(actualSession.getLastAccessedTime()).isNotEqualTo(currentLastAccessedTime); + assertThat(actualSession.getLastAccessedTime().compareTo(expectedCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(actualSession.getAttributeNames()).isEqualTo(Collections.singleton("attrOne")); + assertThat(String.valueOf(actualSession.getAttribute("attrOne"))).isEqualTo("test"); + + verify(this.mockTemplate, times(1)).get(eq(expectedId)); + verify(mockSession, times(1)).isExpired(); + verify(mockSession, times(1)).getId(); + verify(mockSession, times(1)).getCreationTime(); + verify(mockSession, times(1)).getLastAccessedTime(); + verify(mockSession, times(1)).getAttributeNames(); + verify(mockSession, times(1)).getAttribute(eq("attrOne")); + } + + @Test + public void findByIdReturnsNull() { + + when(this.mockTemplate.get(anyString())).thenReturn(null); + + assertThat(this.sessionRepository.findById("1")).isNull(); + + verify(this.mockTemplate, times(1)).get(eq("1")); + } + @Test @SuppressWarnings("unchecked") - public void findByIndexNameAndIndexValueFindsMatchingSession() { + public void findByIndexNameAndIndexValueReturnsMatchingSession() { Session mockSession = mock(Session.class, "MockSession"); @@ -174,7 +283,7 @@ public class GemFireOperationsSessionRepositoryTests { String indexValue = "rwinch"; String expectedQql = - String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_INDEX_VALUE_QUERY, + String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_AND_INDEX_VALUE_QUERY, this.sessionRepository.getFullyQualifiedRegionName(), indexName); given(this.mockTemplate.find(eq(expectedQql), eq(indexValue))).willReturn(mockSelectResults); @@ -193,7 +302,7 @@ public class GemFireOperationsSessionRepositoryTests { @Test @SuppressWarnings("unchecked") - public void findByPrincipalNameFindsMatchingSessions() throws Exception { + public void findByPrincipalNameReturnsMatchingSessions() throws Exception { Session mockSessionOne = mock(Session.class, "MockSessionOne"); Session mockSessionTwo = mock(Session.class, "MockSessionTwo"); @@ -265,7 +374,7 @@ public class GemFireOperationsSessionRepositoryTests { String actualOql = this.sessionRepository.prepareQuery(attributeName); String expectedOql = - String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_INDEX_VALUE_QUERY, + String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_INDEX_NAME_AND_INDEX_VALUE_QUERY, this.sessionRepository.getFullyQualifiedRegionName(), attributeName); assertThat(actualOql).isEqualTo(expectedOql); @@ -274,8 +383,7 @@ public class GemFireOperationsSessionRepositoryTests { @Test public void prepareQueryReturnsPrincipalNameOql() { - String actualQql = - this.sessionRepository.prepareQuery(PRINCIPAL_NAME_INDEX_NAME); + String actualQql = this.sessionRepository.prepareQuery(PRINCIPAL_NAME_INDEX_NAME); String expectedOql = String.format(GemFireOperationsSessionRepository.FIND_SESSIONS_BY_PRINCIPAL_NAME_QUERY, @@ -284,104 +392,6 @@ public class GemFireOperationsSessionRepositoryTests { assertThat(actualQql).isEqualTo(expectedOql); } - @Test - public void createProperlyInitializedSession() { - - Instant beforeOrAtCreationTime = Instant.now(); - - Session session = this.sessionRepository.createSession(); - - assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class); - assertThat(session.getId()).isNotNull(); - assertThat(session.getAttributeNames()).isEmpty(); - assertThat(session.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(session.getLastAccessedTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS)); - } - - @Test - public void getSessionDeletesMatchingExpiredSessionById() { - - String expectedSessionId = "1"; - - Session mockSession = mock(Session.class); - - given(mockSession.isExpired()).willReturn(true); - given(mockSession.getId()).willReturn(expectedSessionId); - given(this.mockTemplate.get(eq(expectedSessionId))).willReturn(mockSession); - given(this.mockTemplate.remove(eq(expectedSessionId))).willReturn(mockSession); - - willAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionDeletedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSession()).isSameAs(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(expectedSessionId); - assertThat(sessionEvent.getSource()) - .isSameAs(GemFireOperationsSessionRepositoryTests.this.sessionRepository); - - return null; - - }).given(this.mockApplicationEventPublisher).publishEvent(any(ApplicationEvent.class)); - - assertThat(this.sessionRepository.findById(expectedSessionId)).isNull(); - - verify(this.mockTemplate, times(1)).get(eq(expectedSessionId)); - verify(this.mockTemplate, times(1)).remove(eq(expectedSessionId)); - verify(mockSession, times(1)).isExpired(); - verify(mockSession, times(2)).getId(); - verify(this.mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionDeletedEvent.class)); - } - - @Test - public void getSessionFindsMatchingNonExpiredSessionById() { - - String expectedId = "1"; - - Instant expectedCreationTime = Instant.now(); - Instant currentLastAccessedTime = expectedCreationTime.plusMillis(TimeUnit.MINUTES.toMillis(5)); - - Session mockSession = mock(Session.class); - - given(mockSession.isExpired()).willReturn(false); - given(mockSession.getId()).willReturn(expectedId); - given(mockSession.getCreationTime()).willReturn(expectedCreationTime); - given(mockSession.getLastAccessedTime()).willReturn(currentLastAccessedTime); - given(mockSession.getAttributeNames()).willReturn(Collections.singleton("attrOne")); - given(mockSession.getAttribute(eq("attrOne"))).willReturn("test"); - given(this.mockTemplate.get(eq(expectedId))).willReturn(mockSession); - - Session actualSession = this.sessionRepository.findById(expectedId); - - assertThat(actualSession).isNotNull(); - assertThat(actualSession).isNotSameAs(mockSession); - assertThat(actualSession.getId()).isEqualTo(expectedId); - assertThat(actualSession.getCreationTime()).isEqualTo(expectedCreationTime); - assertThat(actualSession.getLastAccessedTime()).isNotEqualTo(currentLastAccessedTime); - assertThat(actualSession.getLastAccessedTime().compareTo(expectedCreationTime)).isGreaterThanOrEqualTo(0); - assertThat(actualSession.getAttributeNames()).isEqualTo(Collections.singleton("attrOne")); - assertThat(String.valueOf(actualSession.getAttribute("attrOne"))).isEqualTo("test"); - - verify(this.mockTemplate, times(1)).get(eq(expectedId)); - verify(mockSession, times(1)).isExpired(); - verify(mockSession, times(1)).getId(); - verify(mockSession, times(1)).getCreationTime(); - verify(mockSession, times(1)).getLastAccessedTime(); - verify(mockSession, times(1)).getAttributeNames(); - verify(mockSession, times(1)).getAttribute(eq("attrOne")); - } - - @Test - public void getSessionReturnsNull() { - given(this.mockTemplate.get(anyString())).willReturn(null); - assertThat(this.sessionRepository.findById("1")).isNull(); - } - @Test public void saveIsNullSafe() { @@ -435,20 +445,18 @@ public class GemFireOperationsSessionRepositoryTests { } @Test - public void saveStoresAndThenCommitsGemFireSession() { - - assertThat(this.sessionRepository.isUsingEagerCommit()).isFalse(); + public void saveStoresAndCommitsGemFireSession() { GemFireSession session = spy(GemFireSession.create()); assertThat(session).isNotNull(); - assertThat(session.isDirty()).isTrue(); + assertThat(session.hasDelta()).isTrue(); this.sessionRepository.save(session); InOrder orderVerifier = inOrder(session); - orderVerifier.verify(session, times(2)).isDirty(); + orderVerifier.verify(session, times(2)).hasDelta(); orderVerifier.verify(session, times(1)).getId(); orderVerifier.verify(session, times(1)).commit(); @@ -460,115 +468,19 @@ public class GemFireOperationsSessionRepositoryTests { @SuppressWarnings("unchecked") public void saveWillNotStoreNonDirtyGemFireSessions() { - GemFireSession session = spy(GemFireSession.from(mockSession())); + GemFireSession session = spy(newNonDirtyGemFireSession()); assertThat(session).isNotNull(); assertThat(session.hasDelta()).isFalse(); - assertThat(session.isDirty()).isFalse(); this.sessionRepository.save(session); - verify(session, times(2)).isDirty(); + verify(session, times(2)).hasDelta(); verify(session, never()).getId(); verify(session, never()).commit(); verify(this.mockTemplate, never()).put(any(), any(GemFireSession.class)); } - @Test - public void saveEagerlyCommitsAndThenStoresSession() { - - this.sessionRepository.setUseEagerCommit(true); - - assertThat(this.sessionRepository.isUsingEagerCommit()).isTrue(); - - GemFireSession session = spy(GemFireSession.create()); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isNotEmpty(); - assertThat(session.isDirty()).isTrue(); - assertThat(session.hasDelta()).isFalse(); - assertThat(session.isExpired()).isFalse(); - - when(this.mockTemplate.put(anyString(), any(GemFireSession.class))).thenAnswer(invocation -> { - - String sessionId = invocation.getArgument(0); - GemFireSession sessionToSave = invocation.getArgument(1); - - assertThat(sessionId).isEqualTo(session.getId()); - assertThat(sessionId).isEqualTo(sessionToSave.getId()); - assertThat(sessionToSave).isNotSameAs(session); - - return sessionToSave; - }); - - this.sessionRepository.save(session); - - assertThat(session.isDirty()).isFalse(); - assertThat(session.isExpired()).isFalse(); - - InOrder orderVerifier = inOrder(session); - - orderVerifier.verify(session, times(1)).isDirty(); - orderVerifier.verify(session, times(1)).commit(); - orderVerifier.verify(session, times(2)).getId(); - - verify(this.sessionRepository, times(1)).doSave(eq(session)); - verify(this.mockTemplate, times(1)).put(eq(session.getId()), isA(GemFireSession.class)); - } - - @Test(expected = RuntimeException.class) - public void saveEagerlyCommitsStoresSessionAndResetsDirtyBitOnRuntimeException() { - - this.sessionRepository.setUseEagerCommit(true); - - assertThat(this.sessionRepository.isUsingEagerCommit()).isTrue(); - - GemFireSession session = spy(GemFireSession.create()); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isNotEmpty(); - assertThat(session.isDirty()).isTrue(); - assertThat(session.hasDelta()).isFalse(); - assertThat(session.isExpired()).isFalse(); - - when(this.mockTemplate.put(anyString(), any(GemFireSession.class))).thenAnswer(invocation -> { - - String sessionId = invocation.getArgument(0); - GemFireSession sessionToSave = invocation.getArgument(1); - - assertThat(sessionId).isEqualTo(session.getId()); - assertThat(sessionId).isEqualTo(sessionToSave.getId()); - assertThat(sessionToSave).isNotSameAs(session); - - throw new RuntimeException("TEST"); - }); - - try { - this.sessionRepository.save(session); - } - catch (Exception expected) { - - assertThat(expected).hasMessage("TEST"); - assertThat(expected).hasNoCause(); - - throw expected; - } - finally { - - assertThat(session.isDirty()).isTrue(); - assertThat(session.isExpired()).isFalse(); - - InOrder orderVerifier = inOrder(session); - - orderVerifier.verify(session, times(1)).isDirty(); - orderVerifier.verify(session, times(1)).commit(); - orderVerifier.verify(session, times(2)).getId(); - - verify(this.sessionRepository, times(1)).doSave(eq(session)); - verify(this.mockTemplate, times(1)).put(eq(session.getId()), isA(GemFireSession.class)); - } - } - @Test public void deleteRemovesExistingSessionAndHandlesDelete() { diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java index 5173a70..9291b6c 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionAttributesSerializerTests.java @@ -75,6 +75,9 @@ public class DataSerializableSessionAttributesSerializerTests { GemFireSessionAttributes sessionAttributes = GemFireSessionAttributes.create(); + assertThat(sessionAttributes).isNotNull(); + assertThat(sessionAttributes.hasDelta()).isFalse(); + sessionAttributes.setAttribute("attrOne", "testOne"); sessionAttributes.setAttribute("attrTwo", "testTwo"); @@ -88,8 +91,12 @@ public class DataSerializableSessionAttributesSerializerTests { }).when(sessionAttributesSerializer).serializeObject(any(), any(DataOutput.class)); + assertThat(sessionAttributes.hasDelta()).isTrue(); + sessionAttributesSerializer.serialize(sessionAttributes, mockDataOutput); + assertThat(sessionAttributes.hasDelta()).isTrue(); + verify(mockDataOutput, times(1)).writeInt(eq(2)); verify(mockDataOutput, times(1)).writeUTF(eq("attrOne")); verify(mockDataOutput, times(1)).writeUTF(eq("testOne")); @@ -113,10 +120,11 @@ public class DataSerializableSessionAttributesSerializerTests { GemFireSessionAttributes sessionAttributes = sessionAttributesSerializer.deserialize(mockDataInput); assertThat(sessionAttributes).isNotNull(); - assertThat(sessionAttributes.getAttributeNames()).hasSize(2); + assertThat(sessionAttributes).hasSize(2); assertThat(sessionAttributes.getAttributeNames()).containsAll(asSet("attrOne", "attrTwo")); assertThat(sessionAttributes.getAttribute("attrOne")).isEqualTo("testOne"); assertThat(sessionAttributes.getAttribute("attrTwo")).isEqualTo("testTwo"); + assertThat(sessionAttributes.hasDelta()).isTrue(); verify(mockDataInput, times(1)).readInt(); verify(mockDataInput, times(2)).readUTF(); diff --git a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java index e8ad526..e4ed51d 100644 --- a/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java +++ b/spring-session-data-geode/src/test/java/org/springframework/session/data/gemfire/serialization/data/provider/DataSerializableSessionSerializerTests.java @@ -75,6 +75,7 @@ public class DataSerializableSessionSerializerTests { @Test public void supportedClassContainsGemFireSessionAndSubTypes() { + assertThat(this.sessionSerializer.getSupportedClasses()).contains(GemFireSession.class); assertThat(this.sessionSerializer.getSupportedClasses()).contains(DeltaCapableGemFireSession.class); } @@ -100,7 +101,7 @@ public class DataSerializableSessionSerializerTests { this.sessionSerializer.serialize(session, mockDataOutput); - assertThat(session.hasDelta()).isFalse(); + assertThat(session.hasDelta()).isTrue(); verify(mockDataOutput, times(1)).writeUTF(eq(session.getId())); verify(mockDataOutput, times(1)).writeLong(eq(session.getCreationTime().toEpochMilli())); @@ -150,7 +151,7 @@ public class DataSerializableSessionSerializerTests { assertThat(session.getLastAccessedTime()).isEqualTo(Instant.ofEpochMilli(expectedLastAccessedTime)); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(expectedMaxInactiveIntervalInSeconds)); assertThat(session.getPrincipalName()).isEqualTo(expectedPrincipalName); - assertThat(session.hasDelta()).isFalse(); + assertThat(session.hasDelta()).isTrue(); assertThat(session.getAttributeNames()).hasSize(3); assertThat(session.getAttributeNames()).containsAll(expectedAttributeNames); assertThat(session.getAttribute("attrOne")).isEqualTo("testOne");