Add an IsDirtyPredicate strategy interface for determining when users application domain objects are dirty in a DataSerialization, Delta capable Session configuration.

Resolves gh-17.
This commit is contained in:
John Blum
2018-12-15 10:40:43 -08:00
parent f8c8e5d912
commit 96632a0502
8 changed files with 623 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
/*
* 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 org.apache.geode.Delta;
import org.springframework.lang.Nullable;
/**
* {@link DeltaAwareDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation that evaluates
* the {@link Object new value} as instance of {@link Delta} and uses the {@link Delta#hasDelta()} method
* to determine if the {@link Object new value} is dirty.
*
* @author John Blum
* @see org.apache.geode.Delta
* @see org.springframework.session.data.gemfire.support.IsDirtyPredicate
* @since 2.1.2
*/
@SuppressWarnings("unused")
public class DeltaAwareDirtyPredicate implements IsDirtyPredicate {
public static final DeltaAwareDirtyPredicate INSTANCE = new DeltaAwareDirtyPredicate();
/**
* Determines whether the {@link Object new value} is dirty by evaluating the {@link Object new value}
* as an instance of {@link Delta} and invoking its {@link Delta#hasDelta()} method.
*
* The {@link Object new value} is considered dirty immediately and automatically if the {@link Object new value}
* is not an instance of {@link Delta}.
*
* This method is {@literal null-safe}.
*
* @param oldValue {@link Object} referring to the previous value.
* @param newValue {@link Object} referring to the new value.
* @return a boolean value indicating whether the {@link Object new value} is dirty by evaluating
* the {@link Object new value} as an instance of {@link Delta} and invoking its {@link Delta#hasDelta()} method.
* Returns {@literal true} immediately if the {@link Object new value} is not an instance of {@link Delta}.
* @see org.apache.geode.Delta#hasDelta()
* @see org.apache.geode.Delta
*/
@Override
public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) {
return !(newValue instanceof Delta) || ((Delta) newValue).hasDelta();
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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 org.springframework.lang.Nullable;
/**
* {@link EqualsDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation that determines
* whether the {@link Object new value} is dirty by comparing the {@link Object new value}
* with the {@link Object old value} using the {@link Object#equals(Object)} method.
*
* @author John Blum
* @see org.springframework.session.data.gemfire.support.IsDirtyPredicate
* @since 2.1.2
*/
@SuppressWarnings("unused")
public class EqualsDirtyPredicate implements IsDirtyPredicate {
public static final EqualsDirtyPredicate INSTANCE = new EqualsDirtyPredicate();
/**
* Determines whether the {@link Object new value} is dirty by comparing the {@link Object new value}
* with the {@link Object old value} using the {@link Object#equals(Object)} method.
*
* This method is {@literal null-safe}.
*
* @param oldValue {@link Object} referring to the previous value.
* @param newValue {@link Object} referring to the new value.
* @return a boolean value indicating whether the {@link Object new value} is dirty by comparing
* the {@link Object new value} with the {@link Object old value} using the {@link Object#equals(Object)} method.
* @see java.lang.Object#equals(Object)
*/
@Override
public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) {
return newValue != null
? !newValue.equals(oldValue)
: oldValue != null;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 org.springframework.lang.Nullable;
/**
* {@link IdentityEqualsDirtyPredicate} is an {@link IsDirtyPredicate} strategy interface implementation
* that determines whether the {@link Object new value} is dirty by comparing the {@link Object new value}
* with the {@link Object old value} using the identity operator ({@literal ==}).
*
* @author John Blum
* @see org.springframework.session.data.gemfire.support.IsDirtyPredicate
* @since 2.1.2
*/
@SuppressWarnings("unused")
public class IdentityEqualsDirtyPredicate implements IsDirtyPredicate {
public static final IdentityEqualsDirtyPredicate INSTANCE = new IdentityEqualsDirtyPredicate();
/**
* Determines whether the {@link Object new value} is dirty by comparing the {@link Object new value}
* with the {@link Object old value} using the identity operator ({@literal ==}).
*
* This method is {@literal null-safe}.
*
* @param oldValue {@link Object} referring to the previous value.
* @param newValue {@link Object} referring to the new value.
* @return a boolean value indicating whether the {@link Object new value} is dirty by comparing
* the {@link Object new value} with the {@link Object old value} using the identity operator ({@literal ==}).
*/
@Override
public boolean isDirty(@Nullable Object oldValue, @Nullable Object newValue) {
return newValue != oldValue;
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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 org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
* {@link IsDirtyPredicate} is a strategy interface used to configure Spring Session on how to evaluate application
* domain objects to determine whether they are dirty or not.
*
* @author John Blum
* @since 2.1.2
*/
@FunctionalInterface
@SuppressWarnings("unused")
public interface IsDirtyPredicate {
IsDirtyPredicate ALWAYS_DIRTY = (oldValue, newValue) -> false;
IsDirtyPredicate NEVER_DIRTY = (oldValue, newValue) -> true;
/**
* Determines whether the {@link Object newValue} is dirty relative to the {@link Object oldValue}.
*
* @param oldValue {@link Object} referring to the previous value.
* @param newValue {@link Object} referring to the new value.
* @return a boolean value indicating whether the {@link Object newValue} is dirty
* relative to the {@link Object oldValue}.
*/
boolean isDirty(Object oldValue, Object newValue);
/**
* Composes 2 {@link IsDirtyPredicate} objects using the logical AND operator.
*
* The {@code other} {@link IsDirtyPredicate} is applied after {@literal this} {@link IsDirtyPredicate}
* in the {@link #isDirty(Object, Object)} comparison evaluation.
*
* This composition is {@literal null-safe} and returns {@literal this} {@link IsDirtyPredicate}
* if {@link IsDirtyPredicate other} is {@literal null}.
*
* @param other {@link IsDirtyPredicate} composed with {@literal this} {@link IsDirtyPredicate}.
* @return an {@link IsDirtyPredicate} composition consisting of {@literal this} {@link IsDirtyPredicate}
* composed with the {@code other} {@link IsDirtyPredicate} using the logical AND operator.
* Returns {@literal this} {@link IsDirtyPredicate} if {@link IsDirtyPredicate other} is {@literal null}.
* @see #orThen(IsDirtyPredicate)
*/
default @NonNull IsDirtyPredicate andThen(@Nullable IsDirtyPredicate other) {
return other != null
? (oldValue, newValue) -> this.isDirty(oldValue, newValue) && other.isDirty(oldValue, newValue)
: this;
}
/**
* Composes 2 {@link IsDirtyPredicate} objects using the logical OR operator.
*
* The {@code other} {@link IsDirtyPredicate} is applied after {@literal this} {@link IsDirtyPredicate}
* in the {@link #isDirty(Object, Object)} comparison evaluation.
*
* This composition is {@literal null-safe} and returns {@literal this} {@link IsDirtyPredicate}
* if {@link IsDirtyPredicate other} is {@literal null}.
*
* @param other {@link IsDirtyPredicate} composed with {@literal this} {@link IsDirtyPredicate}.
* @return an {@link IsDirtyPredicate} composition consisting of {@literal this} {@link IsDirtyPredicate}
* composed with the {@code other} {@link IsDirtyPredicate} using the logical OR operator.
* Returns {@literal this} {@link IsDirtyPredicate} if {@link IsDirtyPredicate other} is {@literal null}.
* @see #andThen(IsDirtyPredicate)
*/
default @NonNull IsDirtyPredicate orThen(@Nullable IsDirtyPredicate other) {
return other != null
? (oldValue, newValue) -> this.isDirty(oldValue, newValue) || other.isDirty(oldValue, newValue)
: this;
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.apache.geode.Delta;
/**
* Unit Tests for {@link DeltaAwareDirtyPredicate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.session.data.gemfire.support.DeltaAwareDirtyPredicate
* @since 2.1.2
*/
public class DeltaAwareDirtyPredicateUnitTests {
@Test
public void isDirtyIsNullSafe() {
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, null)).isTrue();
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue();
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue();
}
@Test
public void isDirtyWithNonDeltaObjectReturnsTrue() {
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", "one")).isTrue();
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", "two")).isTrue();
}
@Test
public void isDirtyWithDeltaObjectReturnsTrue() {
Delta mockDelta = mock(Delta.class);
when(mockDelta.hasDelta()).thenReturn(true);
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty(null, mockDelta)).isTrue();
verify(mockDelta, times(1)).hasDelta();
}
@Test
public void isDirtyWithDeltaObjectReturnsFalse() {
Delta mockDelta = mock(Delta.class);
when(mockDelta.hasDelta()).thenReturn(false);
assertThat(DeltaAwareDirtyPredicate.INSTANCE.isDirty("one", mockDelta)).isFalse();
verify(mockDelta, times(1)).hasDelta();
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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 org.junit.Test;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* Unit Tests for {@link EqualsDirtyPredicate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.session.data.gemfire.support.EqualsDirtyPredicate
* @since 2.1.2
*/
public class EqualsDirtyPredicateUnitTests {
@Test
public void isDirtyIsIsNullSafe() {
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(null, null)).isFalse();
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue();
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue();
}
@Test
public void isDirtyWithSameObjectReturnsFalse() {
Entry keyValue = Entry.newEntry(1, "one");
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(keyValue, keyValue));
}
@Test
public void isDirtyWithEqualObjectsReturnsFalse() {
Entry entryOne = Entry.newEntry(1, "one");
Entry entryTwo = Entry.newEntry(1, "one");
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(entryOne, entryTwo));
}
@Test
public void isDirtyWithDifferentObjectsReturnsFalse() {
Entry entryOne = Entry.newEntry(1, "one");
Entry entryTwo = Entry.newEntry(2, "two");
assertThat(EqualsDirtyPredicate.INSTANCE.isDirty(entryOne, entryTwo));
}
@Data
@EqualsAndHashCode
@RequiredArgsConstructor(staticName = "newEntry")
static class Entry {
@NonNull
private final Object key;
@NonNull
private final Object value;
@Override
public String toString() {
return String.format("%1$s = %2$s", getKey(), getValue());
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 org.junit.Test;
/**
* Unit Tests for {@link IdentityEqualsDirtyPredicate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.springframework.session.data.gemfire.support.IdentityEqualsDirtyPredicate
* @since 2.1.2
*/
public class IdentityEqualsDirtyPredicateUnitTests {
@Test
public void isDirtyIsNullSafe() {
assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty(null, null)).isFalse();
assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", null)).isTrue();
assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty(null, "one")).isTrue();
}
@Test
public void isDirtyWithDifferentObjectsReturnsTrue() {
assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", "two")).isTrue();
}
@Test
public void isDirtyWithSameObjectsReturnsFalse() {
assertThat(IdentityEqualsDirtyPredicate.INSTANCE.isDirty("one", "one")).isFalse();
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Test;
/**
* Unit Tests for {@link IsDirtyPredicate}.
*
* @author John Blum
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.session.data.gemfire.support.IsDirtyPredicate
* @since 2.1.2
*/
public class IsDirtyPredicateUnitTests {
@Test
public void alwaysDirtyPredicateIsAlwaysTrue() {
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", "one")).isFalse();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", "two")).isFalse();
}
@Test
public void alwaysDirtyPredicateIsNullSafe() {
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty("one", null)).isFalse();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty(null, "one")).isFalse();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.isDirty(null, null)).isFalse();
}
@Test
public void neverDirtyPredicateIsAlwaysTrue() {
assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", "one")).isTrue();
assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", "two")).isTrue();
}
@Test
public void neverDirtyPredicateIsNullSafe() {
assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty("one", null)).isTrue();
assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty(null, "one")).isTrue();
assertThat(IsDirtyPredicate.NEVER_DIRTY.isDirty(null, null)).isTrue();
}
@Test
public void andThenComposesThisWithNull() {
IsDirtyPredicate dirtyPredicate = mock(IsDirtyPredicate.class);
when(dirtyPredicate.andThen(any())).thenCallRealMethod();
IsDirtyPredicate composedDirtyPredicate = dirtyPredicate.andThen(null);
assertThat(composedDirtyPredicate).isSameAs(dirtyPredicate);
}
@Test
public void andThenComposesTwoIsDirtyPredicates() {
IsDirtyPredicate dirtyPredicateOne = mock(IsDirtyPredicate.class);
IsDirtyPredicate dirtyPredicateTwo = mock(IsDirtyPredicate.class);
when(dirtyPredicateOne.andThen(any())).thenCallRealMethod();
IsDirtyPredicate composedDirtyPredicate = dirtyPredicateOne.andThen(dirtyPredicateTwo);
assertThat(composedDirtyPredicate).isNotNull();
assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateOne);
assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateTwo);
}
@Test
public void andThenIsDirtyReturnsTrue() {
assertThat(IsDirtyPredicate.NEVER_DIRTY.andThen(IsDirtyPredicate.NEVER_DIRTY)
.isDirty("one", "one")).isTrue();
}
@Test
public void andThenIsDirtyReturnsFalse() {
assertThat(IsDirtyPredicate.NEVER_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY)
.isDirty("one", "two")).isFalse();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.NEVER_DIRTY)
.isDirty("one", "two")).isFalse();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY)
.isDirty("one", "two")).isFalse();
}
@Test
public void orThenComposesThisWithNull() {
IsDirtyPredicate dirtyPredicate = mock(IsDirtyPredicate.class);
when(dirtyPredicate.orThen(any())).thenCallRealMethod();
IsDirtyPredicate composedDirtyPredicate = dirtyPredicate.orThen(null);
assertThat(composedDirtyPredicate).isSameAs(dirtyPredicate);
}
@Test
public void orThenComposesTwoIsDirtyPredicates() {
IsDirtyPredicate dirtyPredicateOne = mock(IsDirtyPredicate.class);
IsDirtyPredicate dirtyPredicateTwo = mock(IsDirtyPredicate.class);
when(dirtyPredicateOne.orThen(any())).thenCallRealMethod();
IsDirtyPredicate composedDirtyPredicate = dirtyPredicateOne.orThen(dirtyPredicateTwo);
assertThat(composedDirtyPredicate).isNotNull();
assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateOne);
assertThat(composedDirtyPredicate).isNotSameAs(dirtyPredicateTwo);
}
@Test
public void orThenIsDirtyReturnsTrue() {
assertThat(IsDirtyPredicate.NEVER_DIRTY.orThen(IsDirtyPredicate.NEVER_DIRTY)
.isDirty("one", "one")).isTrue();
assertThat(IsDirtyPredicate.NEVER_DIRTY.orThen(IsDirtyPredicate.ALWAYS_DIRTY)
.isDirty("one", "one")).isTrue();
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.orThen(IsDirtyPredicate.NEVER_DIRTY)
.isDirty("one", "one")).isTrue();
}
@Test
public void orThenIsDirtyReturnsFalse() {
assertThat(IsDirtyPredicate.ALWAYS_DIRTY.andThen(IsDirtyPredicate.ALWAYS_DIRTY)
.isDirty("one", "two")).isFalse();
}
}