diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/support/CustomIsDirtyPredicateIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/support/CustomIsDirtyPredicateIntegrationTests.java new file mode 100644 index 0000000..f3333e6 --- /dev/null +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/support/CustomIsDirtyPredicateIntegrationTests.java @@ -0,0 +1,140 @@ +/* + * 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.support; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.context.annotation.Bean; +import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; +import org.springframework.data.gemfire.tests.mock.annotation.EnableGemFireMockObjects; +import org.springframework.session.data.gemfire.AbstractGemFireIntegrationTests; +import org.springframework.session.data.gemfire.GemFireOperationsSessionRepository; +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; + +/** + * The CustomIsDirtyPredicateIntegrationTests class... + * + * @author John Blum + * @since 1.0.0 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class CustomIsDirtyPredicateIntegrationTests extends AbstractGemFireIntegrationTests { + + @Before + public void setup() { + + GemFireOperationsSessionRepository sessionRepository = getSessionRepository(); + + assertThat(sessionRepository).isNotNull(); + assertThat(sessionRepository.getIsDirtyPredicate()) + .isInstanceOf(OddNumberDirtyPredicateStrategy.class); + } + + @Test + public void isDirtyStrategyIsCorrect() { + + GemFireSession session = commit(createSession()); + + assertThat(session).isNotNull(); + assertThat(session.hasDelta()).isFalse(); + + session.setAttribute("1", 2); + + assertThat(session.getAttributeNames()).containsExactly("1"); + assertThat(session.getAttribute("1")).isEqualTo(2); + assertThat(session.hasDelta()).isFalse(); + + session.setAttribute("2", 1); + + assertThat(session.getAttributeNames()).containsOnly("1", "2"); + assertThat(session.getAttribute("1")).isEqualTo(2); + assertThat(session.getAttribute("2")).isEqualTo(1); + assertThat(session.hasDelta()).isTrue(); + + commit(session); + + assertThat(session.getAttributeNames()).containsOnly("1", "2"); + assertThat(session.hasDelta()).isFalse(); + + session.setAttribute("2", 1); + + assertThat(session.getAttributeNames()).containsOnly("1", "2"); + assertThat(session.getAttribute("1")).isEqualTo(2); + assertThat(session.getAttribute("2")).isEqualTo(1); + assertThat(session.hasDelta()).isTrue(); + + commit(session); + + assertThat(session.getAttributeNames()).containsOnly("1", "2"); + assertThat(session.hasDelta()).isFalse(); + + session.setAttribute("3", 4); + + assertThat(session.getAttributeNames()).containsOnly("1", "2", "3"); + assertThat(session.getAttribute("1")).isEqualTo(2); + assertThat(session.getAttribute("2")).isEqualTo(1); + assertThat(session.getAttribute("3")).isEqualTo(4); + assertThat(session.hasDelta()).isFalse(); + + session.setAttribute("2", 3); + + assertThat(session.getAttributeNames()).containsOnly("1", "2", "3"); + assertThat(session.getAttribute("1")).isEqualTo(2); + assertThat(session.getAttribute("2")).isEqualTo(3); + assertThat(session.getAttribute("3")).isEqualTo(4); + assertThat(session.hasDelta()).isTrue(); + } + + @ClientCacheApplication + @EnableGemFireMockObjects + @EnableGemFireHttpSession( + poolName = "DEFAULT", + sessionSerializerBeanName = GemFireHttpSessionConfiguration.SESSION_DATA_SERIALIZER_BEAN_NAME + ) + @SuppressWarnings("unused") + static class TestConfiguration { + + @Bean + IsDirtyPredicate oddNumberDirtyPredicate() { + return new OddNumberDirtyPredicateStrategy(); + } + } + + static class OddNumberDirtyPredicateStrategy implements IsDirtyPredicate { + + @Override + public boolean isDirty(Object oldValue, Object newValue) { + return toInteger(newValue) % 2 != 0; + } + + private int toInteger(Object value) { + + return value instanceof Number + ? ((Number) value).intValue() + : Integer.parseInt(String.valueOf(value)); + } + } +} 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 0697fdb..2ec025a 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 @@ -96,6 +96,7 @@ import org.slf4j.LoggerFactory; * @see org.springframework.session.SessionRepository * @see org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration * @see org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession + * @see org.springframework.session.data.gemfire.support.DeltaAwareDirtyPredicate * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate * @see org.springframework.session.data.gemfire.support.SessionIdHolder * @see org.springframework.session.events.AbstractSessionEvent @@ -921,7 +922,9 @@ public abstract class AbstractGemFireOperationsSessionRepository } protected synchronized void setIsDirtyPredicate(IsDirtyPredicate dirtyPredicate) { + this.dirtyPredicate = dirtyPredicate; + getAttributes().configureWith(dirtyPredicate); } protected synchronized IsDirtyPredicate getIsDirtyPredicate() { 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 183a182..44a9643 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 @@ -3200,6 +3200,37 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(sessionAttributes.hasDelta()).isFalse(); } + @Test + public void deltaSessionAttributesHasDeltaAnytimeSetAttributeIsCalled() { + + DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); + + sessionAttributes.getMap().put("1", "TEST"); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); + + sessionAttributes.setAttribute("1", "TEST"); + + assertThat(sessionAttributes.getAttribute("1")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).containsExactly("1"); + + sessionAttributes.commit(); + + assertThat(sessionAttributes.getAttribute("1")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); + + sessionAttributes.setAttribute("1", "TEST"); + + assertThat(sessionAttributes.getAttribute("1")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).containsExactly("1"); + } + @Test public void deltaSessionAttributesHasDeltaWhenExistingDeltaObjectHasDeltaIsTrue() { @@ -3228,7 +3259,9 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void deltaSessionAttributesHasDeltaWhenExistingObjectIsReplacedByDeltaObject() { - Delta mockDelta = mock(Delta.class); + Delta mockDelta = mock(Delta.class, withSettings().lenient()); + + when(mockDelta.hasDelta()).thenReturn(false); DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); @@ -3251,7 +3284,9 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void deltaSessionAttributesHasDeltaEvenWhenNonExistingDeltaObjectHasDeltaIsFalse() { - Delta mockDelta = mock(Delta.class); + Delta mockDelta = mock(Delta.class, withSettings().lenient()); + + when(mockDelta.hasDelta()).thenReturn(false); DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); @@ -3318,47 +3353,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void deltaSessionAttributesOnlyHasDeltaWhenIsDirtyPredicateReturnsTrue() { - - Delta mockDelta = mock(Delta.class, withSettings().lenient()); - - when(mockDelta.hasDelta()).thenReturn(true); - - IsDirtyPredicate mockDirtyPredicate = mock(IsDirtyPredicate.class); - - when(mockDirtyPredicate.isDirty(any(), any())).thenReturn(false).thenReturn(true); - - DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); - - sessionAttributes.setIsDirtyPredicate(mockDirtyPredicate); - - assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(mockDirtyPredicate); - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); - assertThat(sessionAttributes.hasDelta()).isFalse(); - assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); - - sessionAttributes.setAttribute("1", mockDelta); - - assertThat(sessionAttributes.getAttributeNames()).containsExactly("1"); - assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); - assertThat(sessionAttributes.hasDelta()).isFalse(); - assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); - - sessionAttributes.setAttribute("2", "TEST"); - - assertThat(sessionAttributes.getAttributeNames()).containsOnly("1", "2"); - assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); - assertThat(sessionAttributes.getAttribute("2")).isEqualTo("TEST"); - assertThat(sessionAttributes.hasDelta()).isTrue(); - assertThat(sessionAttributes.getSessionAttributeDeltas()).containsExactly("2"); - - verify(mockDelta, never()).hasDelta(); - verify(mockDirtyPredicate, times(1)).isDirty(eq(null), eq("TEST")); - verify(mockDirtyPredicate, times(1)).isDirty(eq(null), eq(mockDelta)); - } - - @Test - public void deltaSessionAttributesWillAlwaysHaveDeltaWhenIsDirtyPredicateAlwaysReturnsTrue() { + public void deltaSessionAttributesAlwaysHasDeltaWhenIsDirtyPredicateAlwaysReturnsTrue() { Delta mockDelta = mock(Delta.class, withSettings().lenient()); @@ -3367,9 +3362,11 @@ public class AbstractGemFireOperationsSessionRepositoryTests { DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); sessionAttributes.setIsDirtyPredicate(IsDirtyPredicate.ALWAYS_DIRTY); + sessionAttributes.getMap().put("1", mockDelta); assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(IsDirtyPredicate.ALWAYS_DIRTY); - assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.getAttributeNames()).containsExactly("1"); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); assertThat(sessionAttributes.hasDelta()).isFalse(); assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); @@ -3378,6 +3375,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { sessionAttributes.setAttribute("1", mockDelta); assertThat(sessionAttributes.getAttributeNames()).containsExactly("1"); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); assertThat(sessionAttributes.hasDelta()).isTrue(); assertThat(sessionAttributes.getSessionAttributeDeltas()).containsExactly("1"); @@ -3385,7 +3383,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void deltaSessionAttributesWillNeverHaveDeltaWhenIsDirtyPredicateAlwaysReturnsFalse() { + public void deltaSessionAttributesNeverHasDeltaWhenIsDirtyPredicateAlwaysReturnsFalse() { Delta mockDelta = mock(Delta.class, withSettings().lenient()); @@ -3411,6 +3409,55 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(mockDelta, never()).hasDelta(); } + @Test + public void deltaSessionAttributesOnlyHasDeltaWhenIsDirtyPredicateReturnsTrue() { + + Delta mockDelta = mock(Delta.class, withSettings().lenient()); + + when(mockDelta.hasDelta()).thenReturn(true); + + IsDirtyPredicate mockDirtyPredicate = mock(IsDirtyPredicate.class); + + when(mockDirtyPredicate.isDirty(any(), any())).thenReturn(false).thenReturn(false).thenReturn(true); + + DeltaCapableGemFireSessionAttributes sessionAttributes = new DeltaCapableGemFireSessionAttributes(); + + sessionAttributes.setIsDirtyPredicate(mockDirtyPredicate); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(mockDirtyPredicate); + assertThat(sessionAttributes.getAttributeNames()).isEmpty(); + assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); + + sessionAttributes.setAttribute("1", mockDelta); + + assertThat(sessionAttributes.getAttributeNames()).containsExactly("1"); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); + assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); + + sessionAttributes.setAttribute("2", "TEST"); + + assertThat(sessionAttributes.getAttributeNames()).containsOnly("1", "2"); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); + assertThat(sessionAttributes.getAttribute("2")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).isEmpty(); + + sessionAttributes.setAttribute("2", "TEST"); + + assertThat(sessionAttributes.getAttributeNames()).containsOnly("1", "2"); + assertThat(sessionAttributes.getAttribute("1")).isEqualTo(mockDelta); + assertThat(sessionAttributes.getAttribute("2")).isEqualTo("TEST"); + assertThat(sessionAttributes.hasDelta()).isTrue(); + assertThat(sessionAttributes.getSessionAttributeDeltas()).containsExactly("2"); + + verify(mockDelta, never()).hasDelta(); + verify(mockDirtyPredicate, times(1)).isDirty(eq(null), eq(mockDelta)); + verify(mockDirtyPredicate, times(1)).isDirty(eq(null), eq("TEST")); + verify(mockDirtyPredicate, times(1)).isDirty(eq("TEST"), eq("TEST")); + } + @Test public void sessionAttributesEntrySetIteratesAttributeNamesAndValues() {