From 8e50d365f6feb41c763f5a4a5e00d48b6d3b56b9 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 17 Dec 2018 18:40:17 -0800 Subject: [PATCH] Apply the configured IsDirtyPredicate strategy interface implementation to appliation domain objects stored in Session Attributes. Resolves gh-17. --- ...actGemFireOperationsSessionRepository.java | 265 +++++++++++------ .../GemFireOperationsSessionRepository.java | 21 +- ...mFireOperationsSessionRepositoryTests.java | 269 ++++++++++++++---- ...mFireOperationsSessionRepositoryTests.java | 31 +- 4 files changed, 432 insertions(+), 154 deletions(-) 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 c6507ae..c232f33 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 @@ -35,6 +35,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; import org.apache.geode.DataSerializable; import org.apache.geode.DataSerializer; @@ -59,6 +60,7 @@ import org.springframework.session.Session; import org.springframework.session.SessionRepository; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; import org.springframework.session.data.gemfire.support.GemFireUtils; +import org.springframework.session.data.gemfire.support.IsDirtyPredicate; import org.springframework.session.data.gemfire.support.SessionIdHolder; import org.springframework.session.data.gemfire.support.SessionUtils; import org.springframework.session.events.AbstractSessionEvent; @@ -110,20 +112,27 @@ public abstract class AbstractGemFireOperationsSessionRepository private static final boolean DEFAULT_REGISTER_INTEREST_ENABLED = false; private static final boolean DEFAULT_REGISTER_INTEREST_RECEIVE_VALUES = true; - // TODO - refactor and use non-static variable + // TODO - use non-static variable private static final AtomicBoolean usingDataSerialization = new AtomicBoolean(false); + private static final Duration DEFAULT_MAX_INACTIVE_INTERVAL = + Duration.ofSeconds(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); + private static final InterestResultPolicy DEFAULT_REGISTER_INTEREST_RESULT_POLICY = InterestResultPolicy.NONE; + private static final IsDirtyPredicate DEFAULT_IS_DIRTY_PREDICATE = + GemFireHttpSessionConfiguration.DEFAULT_IS_DIRTY_PREDICATE; + private boolean registerInterestEnabled = DEFAULT_REGISTER_INTEREST_ENABLED; private ApplicationEventPublisher applicationEventPublisher = event -> {}; - private Duration maxInactiveInterval = - Duration.ofSeconds(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); + private Duration maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL; private final GemfireOperations template; + private IsDirtyPredicate dirtyPredicate = DEFAULT_IS_DIRTY_PREDICATE; + private final Log logger = newLogger(); private final Region sessions; @@ -213,6 +222,17 @@ public abstract class AbstractGemFireOperationsSessionRepository return sessionsRegion; } + /** + * Constructs a new instance of {@link Log} using Apache Commons {@link LogFactory}. + * + * @return a new instance of {@link Log} constructed from Apache commons-logging {@link LogFactory}. + * @see org.apache.commons.logging.LogFactory#getLog(Class) + * @see org.apache.commons.logging.Log + */ + private Log newLogger() { + return LogFactory.getLog(getClass()); + } + /** * Constructs a new instance of {@link SessionEventHandlerCacheListenerAdapter}. * @@ -233,17 +253,6 @@ public abstract class AbstractGemFireOperationsSessionRepository return new SessionIdInterestRegisteringCacheListener(this); } - /** - * Constructs a new instance of {@link Log} using Apache Commons {@link LogFactory}. - * - * @return a new instance of {@link Log} constructed from Apache commons-logging {@link LogFactory}. - * @see org.apache.commons.logging.LogFactory#getLog(Class) - * @see org.apache.commons.logging.Log - */ - private Log newLogger() { - return LogFactory.getLog(getClass()); - } - /** * Sets the configured {@link ApplicationEventPublisher} used to publish {@link Session} * {@link AbstractSessionEvent events} corresponding to Apache Geode/Pivotal GemFire cache events. @@ -280,11 +289,40 @@ public abstract class AbstractGemFireOperationsSessionRepository * used to store and manage {@link Session} data. * @see #getSessionsRegion() */ - // TODO - refactor and rename to SessionRegionName + // TODO - rename to SessionRegionName protected String getFullyQualifiedRegionName() { return getSessionsRegion().getFullPath(); } + /** + * Configures the {@link IsDirtyPredicate} strategy interface used to determine whether the users' application + * domain objects are dirty or not. + * + * @param dirtyPredicate {@link IsDirtyPredicate} strategy interface implementation used to determine whether + * the users' application domain objects are dirty or not. + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + */ + public void setIsDirtyPredicate(IsDirtyPredicate dirtyPredicate) { + this.dirtyPredicate = dirtyPredicate; + } + + /** + * Returns the configured {@link IsDirtyPredicate} strategy interface implementation used to determine whether + * the users' application domain objects are dirty or not. + * + * Defaults to {@link GemFireHttpSessionConfiguration#DEFAULT_IS_DIRTY_PREDICATE}. + * + * @return the configured {@link IsDirtyPredicate} strategy interface used to determine whether + * the users' application domain objects are dirty or not. + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + */ + public IsDirtyPredicate getIsDirtyPredicate() { + + return this.dirtyPredicate != null + ? this.dirtyPredicate + : DEFAULT_IS_DIRTY_PREDICATE; + } + /** * Return a reference to the {@link Log} used to log messages. * @@ -431,6 +469,16 @@ public abstract class AbstractGemFireOperationsSessionRepository .orElse(session); } + protected @Nullable Session configure(@Nullable Session session) { + + return Optional.ofNullable(session) + .filter(GemFireSession.class::isInstance) + .map(GemFireSession.class::cast) + .map(it -> it.configureWith(getMaxInactiveInterval())) + .map(it -> it.configureWith(getIsDirtyPredicate())) + .orElse(session); + } + /** * Deletes the given {@link Session} from Apache Geode / Pivotal GemFire. * @@ -510,7 +558,7 @@ public abstract class AbstractGemFireOperationsSessionRepository * @see org.apache.geode.cache.Region#registerInterest(Object, InterestResultPolicy, boolean, boolean) * @see #isRegisterInterestEnabled() */ - protected void registerInterest(Object sessionId) { + protected void registerInterest(@Nullable Object sessionId) { Optional.ofNullable(sessionId) .filter(it -> this.isRegisterInterestEnabled()) @@ -592,7 +640,9 @@ public abstract class AbstractGemFireOperationsSessionRepository @Override protected DeltaCapableGemFireSessionAttributes newSessionAttributes(Object lock) { - return new DeltaCapableGemFireSessionAttributes(lock); + + return new DeltaCapableGemFireSessionAttributes(lock) + .configureWith(getIsDirtyPredicate()); } public synchronized void toDelta(DataOutput out) throws IOException { @@ -618,52 +668,29 @@ public abstract class AbstractGemFireOperationsSessionRepository * * @see java.lang.Comparable * @see org.springframework.session.Session + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSessionAttributes */ @SuppressWarnings("serial") public static class GemFireSession implements Comparable, Session { - protected static final Duration DEFAULT_MAX_INACTIVE_INTERVAL = Duration.ZERO; - 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 }"; 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}. + * Factory method used to construct a new, default instance of {@link GemFireSession}. * * @param {@link Class Sub-type} of {@link GemFireSessionAttributes}. - * @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 {@link Class Sub-type} of {@link GemFireSessionAttributes}. - * @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}. + * @return a new {@link GemFireSession}. * @see #isUsingDataSerialization() - * @see java.time.Duration */ @SuppressWarnings("unchecked") - // TODO - remove - public static GemFireSession create(Duration maxInactiveInterval) { + public static GemFireSession create() { - GemFireSession session = isUsingDataSerialization() - ? new DeltaCapableGemFireSession() + return isUsingDataSerialization() + ? (GemFireSession) new DeltaCapableGemFireSession() : new GemFireSession(); - - session.setMaxInactiveInterval(maxInactiveInterval); - - return session; } /** @@ -682,12 +709,12 @@ public abstract class AbstractGemFireOperationsSessionRepository } /** - * 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}. + * 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 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} + * @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") @@ -700,8 +727,11 @@ public abstract class AbstractGemFireOperationsSessionRepository private Duration maxInactiveInterval; private final Instant creationTime; + private Instant lastAccessedTime; + private transient IsDirtyPredicate dirtyPredicate = DEFAULT_IS_DIRTY_PREDICATE; + private transient final SpelExpressionParser parser = new SpelExpressionParser(); private String id; @@ -734,7 +764,7 @@ public abstract class AbstractGemFireOperationsSessionRepository this.id = validateSessionId(id); this.creationTime = Instant.now(); this.lastAccessedTime = this.creationTime; - this.maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL; + this.maxInactiveInterval = Duration.ZERO; } /** @@ -761,10 +791,13 @@ public abstract class AbstractGemFireOperationsSessionRepository * @param lock {@link Object} used as the mutex for concurrent access and Thread-safety. * @return the new {@link GemFireSessionAttributes}. * @see GemFireSessionAttributes + * @see #getIsDirtyPredicate() */ @SuppressWarnings("unchecked") protected T newSessionAttributes(Object lock) { - return (T) new GemFireSessionAttributes(lock); + + return (T) new GemFireSessionAttributes(lock) + .configureWith(getIsDirtyPredicate()); } /** @@ -815,6 +848,17 @@ public abstract class AbstractGemFireOperationsSessionRepository getAttributes().commit(); } + /** + * Determines whether this {@link GemFireSession} has any changes (i.e. a delta). + * + * Changes exist if this {@link GemFireSession GemFireSession's} {@link #getId() ID}, + * {@link #getLastAccessedTime() last accessed time}, {@link #getMaxInactiveInterval() max inactive interval} + * or any of these {@link #getAttributeNames() attributes} have changed. + * + * @return a boolean value indicating whether this {@link GemFireSession} has any changes. + * @see GemFireSessionAttributes#hasDelta() + * @see #getAttributes() + */ public synchronized boolean hasDelta() { return this.delta || getAttributes().hasDelta(); } @@ -877,6 +921,17 @@ public abstract class AbstractGemFireOperationsSessionRepository return !isExpirationDisabled(duration); } + protected synchronized void setIsDirtyPredicate(IsDirtyPredicate dirtyPredicate) { + this.dirtyPredicate = dirtyPredicate; + } + + protected synchronized IsDirtyPredicate getIsDirtyPredicate() { + + return this.dirtyPredicate != null + ? this.dirtyPredicate + : DEFAULT_IS_DIRTY_PREDICATE; + } + private boolean isLastAccessedTimeValid(Instant lastAccessedTime) { return lastAccessedTime != null; } @@ -904,8 +959,9 @@ public abstract class AbstractGemFireOperationsSessionRepository public synchronized Duration getMaxInactiveInterval() { - return Optional.ofNullable(this.maxInactiveInterval) - .orElse(DEFAULT_MAX_INACTIVE_INTERVAL); + return this.maxInactiveInterval != null + ? this.maxInactiveInterval + : Duration.ZERO; } public synchronized void setPrincipalName(String principalName) { @@ -931,6 +987,36 @@ public abstract class AbstractGemFireOperationsSessionRepository return principalName; } + /** + * Builder method to configure the {@link Duration max inactive interval} before this {@link GemFireSession} + * will expire. + * + * @param maxInactiveInterval {@link Duration} specifying the maximum time this {@link GemFireSession} + * can remain inactive before expiration. + * @return this {@link GemFireSession}. + * @see #setMaxInactiveInterval(Duration) + * @see java.time.Duration + */ + public GemFireSession configureWith(Duration maxInactiveInterval) { + setMaxInactiveInterval(maxInactiveInterval); + return this; + } + + /** + * Builder method to configure the {@link IsDirtyPredicate} strategy interface implementation to determine + * whether users' {@link Object application domain objects} stored in this {@link GemFireSession} are dirty. + * + * @param dirtyPredicate {@link IsDirtyPredicate} strategy interface implementation that determines whether + * the users' {@link Object application domain objects} stored in this {@link GemFireSession} are dirty. + * @return this {@link GemFireSession}. + * @see org.springframework.session.data.gemfire.support.IsDirtyPredicate + * @see #setIsDirtyPredicate(IsDirtyPredicate) + */ + public GemFireSession configureWith(IsDirtyPredicate dirtyPredicate) { + setIsDirtyPredicate(dirtyPredicate); + return this; + } + @SuppressWarnings("all") @Override public int compareTo(Session session) { @@ -971,7 +1057,6 @@ public abstract class AbstractGemFireOperationsSessionRepository } } - @SuppressWarnings("unused") public static class DeltaCapableGemFireSessionAttributes extends GemFireSessionAttributes implements Delta { private transient final Set sessionAttributeDeltas = new HashSet<>(); @@ -990,38 +1075,12 @@ public abstract class AbstractGemFireOperationsSessionRepository } @Override - public Object setAttribute(String attributeName, Object attributeValue) { + protected BiFunction sessionAttributesChangeInterceptor() { - synchronized (getLock()) { - - if (attributeValue != null) { - - Object previousAttributeValue = super.setAttribute(attributeName, attributeValue); - - if (!attributeValue.equals(previousAttributeValue)) { - getSessionAttributeDeltas().add(attributeName); - } - - return previousAttributeValue; - } - else { - return removeAttribute(attributeName); - } - } - } - - @Override - public Object removeAttribute(String attributeName) { - - synchronized (getLock()) { - - return Optional.ofNullable(super.removeAttribute(attributeName)) - .map(previousAttributeValue -> { - getSessionAttributeDeltas().add(attributeName); - return previousAttributeValue; - }) - .orElse(null); - } + return (attributeName, attributeValue) -> { + getSessionAttributeDeltas().add(attributeName); + return true; + }; } public void toDelta(DataOutput out) throws IOException { @@ -1116,6 +1175,8 @@ public abstract class AbstractGemFireOperationsSessionRepository private transient boolean delta = false; + private transient IsDirtyPredicate dirtyPredicate = DEFAULT_IS_DIRTY_PREDICATE; + private transient final Map sessionAttributes = new HashMap<>(); private transient final Object lock; @@ -1149,6 +1210,22 @@ public abstract class AbstractGemFireOperationsSessionRepository return this.lock; } + protected void setIsDirtyPredicate(IsDirtyPredicate dirtyPredicate) { + + synchronized (getLock()) { + this.dirtyPredicate = dirtyPredicate; + } + } + + protected IsDirtyPredicate getIsDirtyPredicate() { + + synchronized (getLock()) { + return this.dirtyPredicate != null + ? this.dirtyPredicate + : DEFAULT_IS_DIRTY_PREDICATE; + } + } + public Object setAttribute(String attributeName, Object attributeValue) { synchronized (getLock()) { @@ -1162,7 +1239,8 @@ public abstract class AbstractGemFireOperationsSessionRepository Object previousAttributeValue = this.sessionAttributes.put(attributeName, attributeValue); - this.delta |= !attributeValue.equals(previousAttributeValue); + this.delta |= getIsDirtyPredicate().isDirty(previousAttributeValue, attributeValue) + && sessionAttributesChangeInterceptor().apply(attributeName, attributeValue); return previousAttributeValue; } @@ -1171,7 +1249,8 @@ public abstract class AbstractGemFireOperationsSessionRepository synchronized (getLock()) { - this.delta |= this.sessionAttributes.containsKey(attributeName); + this.delta |= this.sessionAttributes.containsKey(attributeName) + && sessionAttributesChangeInterceptor().apply(attributeName, null); return this.sessionAttributes.remove(attributeName); } @@ -1214,6 +1293,10 @@ public abstract class AbstractGemFireOperationsSessionRepository } } + protected BiFunction sessionAttributesChangeInterceptor() { + return (attributeName, attributeValue) -> true; + } + protected void commit() { synchronized (getLock()) { @@ -1221,6 +1304,12 @@ public abstract class AbstractGemFireOperationsSessionRepository } } + @SuppressWarnings("unchecked") + public T configureWith(IsDirtyPredicate dirtyPredicate) { + setIsDirtyPredicate(dirtyPredicate); + return (T) this; + } + public void from(Session session) { synchronized (getLock()) { 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 7a46a19..c51a7e8 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 @@ -16,7 +16,6 @@ package org.springframework.session.data.gemfire; -import java.time.Duration; import java.util.HashMap; import java.util.Map; @@ -66,13 +65,13 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation * Constructs a new {@link Session} instance backed by GemFire. * * @return an instance of {@link Session} backed by GemFire. - * @see AbstractGemFireOperationsSessionRepository.GemFireSession#create(Duration) + * @see AbstractGemFireOperationsSessionRepository.GemFireSession#create() * @see org.springframework.session.Session - * @see #getMaxInactiveIntervalInSeconds() + * @see #configure(Session) */ @NonNull public Session createSession() { - return GemFireSession.create(getMaxInactiveInterval()); + return configure(GemFireSession.create()); } /** @@ -86,7 +85,8 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation * @see AbstractGemFireOperationsSessionRepository.GemFireSession#from(Session) * @see org.springframework.session.Session * @see #commit(Session) - * @see #deleteById(String) + * @see #configure(Session) + * @see #delete(Session) * @see #registerInterest(Session) * @see #touch(Session) */ @@ -98,7 +98,7 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation if (storedSession != null) { storedSession = storedSession.isExpired() ? delete(storedSession) - : registerInterest(touch(commit(GemFireSession.from(storedSession)))); + : touch(commit(registerInterest(configure(GemFireSession.from(storedSession))))); } return storedSession; @@ -114,9 +114,10 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation * (e.g. {@literal username}). * @return a mapping of {@link Session#getId()} Session IDs} to {@link Session} objects. * @see org.springframework.session.Session - * @see #prepareQuery(String) * @see java.util.Map + * @see #prepareQuery(String) * @see #commit(Session) + * @see #configure(Session) * @see #registerInterest(Session) * @see #touch(Session) */ @@ -127,7 +128,8 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation Map sessions = new HashMap<>(results.size()); - results.asList().forEach(session -> sessions.put(session.getId(), registerInterest(touch(commit(session))))); + results.asList().forEach(session -> + sessions.put(session.getId(), touch(commit(registerInterest(configure(session)))))); return sessions; } @@ -139,6 +141,7 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation * @param indexName a String indicating the name of the indexed Session attribute. * @return an appropriate Pivotal GemFire OQL statement for querying on a particular indexed * Session attribute. + * @see #getFullyQualifiedRegionName() */ protected String prepareQuery(String indexName) { @@ -159,6 +162,8 @@ public class GemFireOperationsSessionRepository extends AbstractGemFireOperation * @param session the {@link Session} to save. * @see org.springframework.data.gemfire.GemfireOperations#put(Object, Object) * @see org.springframework.session.Session + * @see #isNonNullAndDirty(Session) + * @see #doSave(Session) */ public void save(@Nullable Session 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 17d199f..c49410b 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 @@ -86,7 +86,11 @@ import org.springframework.data.gemfire.util.RegionUtils; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; import org.springframework.session.data.gemfire.config.annotation.web.http.GemFireHttpSessionConfiguration; +import org.springframework.session.data.gemfire.support.DeltaAwareDirtyPredicate; +import org.springframework.session.data.gemfire.support.EqualsDirtyPredicate; import org.springframework.session.data.gemfire.support.GemFireOperationsSessionRepositorySupport; +import org.springframework.session.data.gemfire.support.IdentityEqualsDirtyPredicate; +import org.springframework.session.data.gemfire.support.IsDirtyPredicate; import org.springframework.session.data.gemfire.support.SessionIdHolder; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionCreatedEvent; @@ -336,6 +340,26 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(this.mockRegion, times(1)).getFullPath(); } + @Test + public void setAndGetIsDirtyPredicate() { + + assertThat(this.sessionRepository.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + IsDirtyPredicate mockDirtyPredicate = mock(IsDirtyPredicate.class); + + this.sessionRepository.setIsDirtyPredicate(mockDirtyPredicate); + + assertThat(this.sessionRepository.getIsDirtyPredicate()).isEqualTo(mockDirtyPredicate); + + this.sessionRepository.setIsDirtyPredicate(null); + + assertThat(this.sessionRepository.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + this.sessionRepository.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + + assertThat(this.sessionRepository.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + } + @Test public void setAndGetMaxInactiveInterval() { @@ -374,7 +398,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void maxInactiveIntervalInSecondsAllowsExtremelyLargeAndNegativeValues() { + public void setMaxInactiveIntervalInSecondsAllowsExtremelyLargeAndNegativeValues() { assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()) .isEqualTo(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); @@ -472,6 +496,44 @@ public class AbstractGemFireOperationsSessionRepositoryTests { this.sessionRepository.commit(null); } + @Test + public void configureWithGemFireSession() { + + GemFireSession session = GemFireSession.create(); + + assertThat(session).isNotNull(); + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); + + this.sessionRepository.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + this.sessionRepository.setMaxInactiveIntervalInSeconds(300); + this.sessionRepository.configure(session); + + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(300)); + assertThat(session.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + + verify(this.sessionRepository, times(1)).getIsDirtyPredicate(); + verify(this.sessionRepository, times(1)).getMaxInactiveInterval(); + } + + @Test + public void configureWithNull() { + + this.sessionRepository.configure(null); + + verify(this.sessionRepository, never()).getIsDirtyPredicate(); + verify(this.sessionRepository, never()).getMaxInactiveInterval(); + } + + @Test + public void configureWithSession() { + + this.sessionRepository.configure(this.mockSession); + + verify(this.sessionRepository, never()).getIsDirtyPredicate(); + verify(this.sessionRepository, never()).getMaxInactiveInterval(); + } + @Test public void deleteSessionCallsDeleteSessionById() { @@ -1902,37 +1964,46 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void createNewGemFireSessionWithDefaultMaxInactiveInterval() { + public void createNewGemFireSession() { + + assertThat(AbstractGemFireOperationsSessionRepository.isUsingDataSerialization()).isFalse(); Instant testCreationTime = Instant.now(); GemFireSession session = GemFireSession.create(); assertThat(session).isNotNull(); - assertThat(session.getId()).isNotNull(); + assertThat(session).isNotInstanceOf(DeltaCapableGemFireSession.class); + assertThat(session.getId()).isNotEmpty(); assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.getCreationTime()).isBeforeOrEqualTo(Instant.now()); assertThat(session.hasDelta()).isTrue(); + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); - assertThat(session.getMaxInactiveInterval()).isEqualTo(GemFireSession.DEFAULT_MAX_INACTIVE_INTERVAL); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); assertThat(session.getAttributes()).isNotNull(); assertThat(session.getAttributes()).isEmpty(); } @Test - public void createNewGemFireSessionWithSpecifiedMaxInactiveInterval() { + public void createNewDeltaCapableGemFireSession() { + + this.sessionRepository.setUseDataSerialization(true); + + assertThat(AbstractGemFireOperationsSessionRepository.isUsingDataSerialization()).isTrue(); Instant testCreationTime = Instant.now(); - Duration maxInactiveInterval = Duration.ofSeconds(120L); + GemFireSession session = GemFireSession.create(); - GemFireSession session = GemFireSession.create(maxInactiveInterval); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isNotNull(); + assertThat(session).isInstanceOf(DeltaCapableGemFireSession.class); + assertThat(session.getId()).isNotEmpty(); assertThat(session.getCreationTime()).isAfterOrEqualTo(testCreationTime); + assertThat(session.getCreationTime()).isBeforeOrEqualTo(Instant.now()); assertThat(session.hasDelta()).isTrue(); + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); - assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactiveInterval); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); assertThat(session.getAttributes()).isNotNull(); assertThat(session.getAttributes()).isEmpty(); } @@ -2013,6 +2084,16 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(mockSession, times(1)).getAttribute(eq("attributeOne")); } + @Test + public void fromExistingGemFireSessionIsGemFireSession() { + + GemFireSession gemfireSession = GemFireSession.create(); + + GemFireSession fromGemFireSession = GemFireSession.from(gemfireSession); + + assertThat(fromGemFireSession).isSameAs(gemfireSession); + } + @Test public void fromExistingSessionCopiesSession() { @@ -2045,16 +2126,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { verify(mockSession, never()).getAttribute(anyString()); } - @Test - public void fromExistingGemFireSessionIsGemFireSession() { - - GemFireSession gemfireSession = GemFireSession.create(); - - GemFireSession fromGemFireSession = GemFireSession.from(gemfireSession); - - assertThat(fromGemFireSession).isSameAs(gemfireSession); - } - @Test(expected = IllegalArgumentException.class) public void fromNullSessionThrowsIllegalArgumentException() { @@ -2070,6 +2141,50 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } } + @Test + public void newSessionAttributesIsConfiguredCorrectly() { + + GemFireSession session = new GemFireSession<>(); + + session.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + + GemFireSessionAttributes sessionAttributes = session.newSessionAttributes(session); + + assertThat(sessionAttributes).isNotNull(); + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + assertThat(sessionAttributes.getLock()).isSameAs(session); + } + + @Test + public void newDeltaCapableSessionAttributesIsConfiguredCorrectly() { + + DeltaCapableGemFireSession session = new DeltaCapableGemFireSession(); + + session.setIsDirtyPredicate(IdentityEqualsDirtyPredicate.INSTANCE); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(IdentityEqualsDirtyPredicate.INSTANCE); + + DeltaCapableGemFireSessionAttributes sessionAttributes = session.newSessionAttributes(session); + + assertThat(sessionAttributes).isNotNull(); + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(IdentityEqualsDirtyPredicate.INSTANCE); + assertThat(sessionAttributes.getLock()).isEqualTo(session); + } + + @Test + public void changeSessionIdIsCorrect() { + + GemFireSession session = new GemFireSession<>(); + + String sessionId = session.getId(); + + assertThat(sessionId).isNotEmpty(); + assertThat(session.changeSessionId()).isNotEmpty(); + assertThat(session.getId()).isNotEqualTo(sessionId); + } + @Test public void setGetAndRemoveAttribute() { @@ -2108,7 +2223,8 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Duration expectedMaxInactiveIntervalInSeconds = Duration.ofSeconds(-1); - GemFireSession session = GemFireSession.create(expectedMaxInactiveIntervalInSeconds); + GemFireSession session = GemFireSession.create() + .configureWith(expectedMaxInactiveIntervalInSeconds); assertThat(session).isNotNull(); assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveIntervalInSeconds); @@ -2118,25 +2234,23 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void isExpiredWhenMaxInactiveIntervalIsNullReturnsFalse() { - GemFireSession session = GemFireSession.create(null); + GemFireSession session = GemFireSession.create(); assertThat(session).isNotNull(); session.setMaxInactiveInterval(null); - assertThat(session.getMaxInactiveInterval()).isEqualTo(GemFireSession.DEFAULT_MAX_INACTIVE_INTERVAL); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); assertThat(session.isExpired()).isFalse(); } @Test public void isExpiredWhenMaxInactiveIntervalIsZeroReturnsFalse() { - Duration expectedMaxInactiveIntervalInSeconds = Duration.ZERO; - - GemFireSession session = GemFireSession.create(expectedMaxInactiveIntervalInSeconds); + GemFireSession session = GemFireSession.create(); assertThat(session).isNotNull(); - assertThat(session.getMaxInactiveInterval()).isEqualTo(expectedMaxInactiveIntervalInSeconds); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); assertThat(session.isExpired()).isFalse(); } @@ -2145,7 +2259,8 @@ public class AbstractGemFireOperationsSessionRepositoryTests { long expectedMaxInactiveIntervalInSeconds = TimeUnit.HOURS.toSeconds(2); - GemFireSession session = GemFireSession.create(Duration.ofSeconds(expectedMaxInactiveIntervalInSeconds)); + GemFireSession session = GemFireSession.create() + .configureWith(Duration.ofSeconds(expectedMaxInactiveIntervalInSeconds)); assertThat(session).isNotNull(); assertThat(session.getMaxInactiveInterval()) @@ -2164,7 +2279,8 @@ public class AbstractGemFireOperationsSessionRepositoryTests { Duration maxInactiveInterval = Duration.ofMillis(1); - GemFireSession session = GemFireSession.create(maxInactiveInterval); + GemFireSession session = GemFireSession.create() + .configureWith(maxInactiveInterval); assertThat(session).isNotNull(); assertThat(session.getMaxInactiveInterval()).isEqualTo(maxInactiveInterval); @@ -2179,6 +2295,29 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.isExpired()).isTrue(); } + @Test + public void setAndGetGemFireSessionIsDirtyPredicate() { + + GemFireSession session = GemFireSession.create(); + + assertThat(session).isNotNull(); + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + IsDirtyPredicate mockDirtyPredicate = mock(IsDirtyPredicate.class); + + session.setIsDirtyPredicate(mockDirtyPredicate); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(mockDirtyPredicate); + + session.setIsDirtyPredicate(null); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + session.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + } + @Test public void setAndGetLastAccessedTime() { @@ -2271,6 +2410,26 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.getPrincipalName()).isNull(); } + @Test + public void configuresIsDirtyPredicateReturnsGemFireSession() { + + GemFireSession session = new GemFireSession<>(); + + assertThat(session.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + assertThat(session.configureWith(EqualsDirtyPredicate.INSTANCE)).isSameAs(session); + assertThat(session.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + } + + @Test + public void configuresMaxInactiveIntervalReturnsGemFireSession() { + + GemFireSession session = new GemFireSession<>(); + + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ZERO); + assertThat(session.configureWith(Duration.ofSeconds(1))).isSameAs(session); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(1)); + } + @Test public void sessionToDelta() throws Exception { @@ -2714,6 +2873,28 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(target.getAttribute("attributeTwo")).isEqualTo("testTwo"); } + @Test + public void setAndGetGemFireSessionAttributesIsDirtyPredicate() { + + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + IsDirtyPredicate mockDirtyPredicate = mock(IsDirtyPredicate.class); + + sessionAttributes.setIsDirtyPredicate(mockDirtyPredicate); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(mockDirtyPredicate); + + sessionAttributes.setIsDirtyPredicate(null); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + + sessionAttributes.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); + } + @Test public void sessionAttributesToDelta() throws Exception { @@ -2750,7 +2931,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { reset(mockDataOutput); sessionAttributes.commit(); - sessionAttributes.setAttribute("attributeOne", "testOne"); assertThat(sessionAttributes.hasDelta()).isFalse(); @@ -2893,7 +3073,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void sessionAttributesHasDeltaWhenSetDoesNotModifyAttributeReturnsFalse() { + public void sessionAttributesHasDeltaWhenSetDoesNotModifyAttributeReturnsTrue() { GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); @@ -2908,7 +3088,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(sessionAttributes.getAttributeNames()).containsExactly("attributeOne"); assertThat(sessionAttributes.getAttribute("attributeOne")).isEqualTo("testOne"); - assertThat(sessionAttributes.hasDelta()).isFalse(); + assertThat(sessionAttributes.hasDelta()).isTrue(); sessionAttributes.commit(); @@ -3071,25 +3251,14 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void gemfireSessionIsLockForGemFireSessionAttributes() { + public void configuresIsDirtyPredicateReturnsGemFireSessionAttributes() { - GemFireSession session = new GemFireSession(); + GemFireSessionAttributes sessionAttributes = new GemFireSessionAttributes(); - GemFireSessionAttributes sessionAttributes = session.newSessionAttributes(session); - - assertThat(sessionAttributes).isNotNull(); - assertThat(sessionAttributes.getLock()).isSameAs(session); - } - - @Test - public void deltaCapableGemFireSessionIsLockForDeltaCapableGemFirSessionAttributes() { - - DeltaCapableGemFireSession session = new DeltaCapableGemFireSession(); - - DeltaCapableGemFireSessionAttributes sessionAttributes = session.newSessionAttributes(session); - - assertThat(sessionAttributes).isNotNull(); - assertThat(sessionAttributes.getLock()).isSameAs(session); + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(DeltaAwareDirtyPredicate.INSTANCE); + assertThat(sessionAttributes.configureWith(EqualsDirtyPredicate.INSTANCE)) + .isSameAs(sessionAttributes); + assertThat(sessionAttributes.getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); } @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 1442c11..a7b7ba0 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 @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME; +import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession; import static org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.GemFireSession; import java.time.Duration; @@ -61,6 +62,8 @@ import org.springframework.data.gemfire.GemfireOperations; import org.springframework.data.gemfire.util.RegionUtils; import org.springframework.session.Session; import org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository.SessionEventHandlerCacheListenerAdapter; +import org.springframework.session.data.gemfire.support.EqualsDirtyPredicate; +import org.springframework.session.data.gemfire.support.IdentityEqualsDirtyPredicate; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionDeletedEvent; @@ -174,14 +177,17 @@ public class GemFireOperationsSessionRepositoryTests { Instant beforeCreationTime = Instant.now(); + this.sessionRepository.setIsDirtyPredicate(EqualsDirtyPredicate.INSTANCE); + Session session = this.sessionRepository.createSession(); - assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.GemFireSession.class); + assertThat(session).isInstanceOf(GemFireSession.class); assertThat(session.getId()).isNotEmpty(); assertThat(session.getAttributeNames()).isEmpty(); assertThat(session.getCreationTime()).isAfterOrEqualTo(beforeCreationTime); assertThat(session.getCreationTime()).isBeforeOrEqualTo(Instant.now()); assertThat(session.isExpired()).isFalse(); + assertThat(((GemFireSession) session).getIsDirtyPredicate()).isEqualTo(EqualsDirtyPredicate.INSTANCE); assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS)); } @@ -191,18 +197,22 @@ public class GemFireOperationsSessionRepositoryTests { Instant beforeCreationTime = Instant.now(); + this.sessionRepository.setIsDirtyPredicate(IdentityEqualsDirtyPredicate.INSTANCE); + this.sessionRepository.setMaxInactiveIntervalInSeconds(300); this.sessionRepository.setUseDataSerialization(true); Session session = this.sessionRepository.createSession(); - assertThat(session).isInstanceOf(AbstractGemFireOperationsSessionRepository.DeltaCapableGemFireSession.class); + assertThat(session).isInstanceOf(DeltaCapableGemFireSession.class); assertThat(session.getId()).isNotEmpty(); assertThat(session.getAttributeNames()).isEmpty(); assertThat(session.getCreationTime()).isAfterOrEqualTo(beforeCreationTime); assertThat(session.getCreationTime()).isBeforeOrEqualTo(Instant.now()); assertThat(session.isExpired()).isFalse(); + assertThat(((DeltaCapableGemFireSession) session).getIsDirtyPredicate()) + .isEqualTo(IdentityEqualsDirtyPredicate.INSTANCE); assertThat(session.getLastAccessedTime()).isEqualTo(session.getCreationTime()); - assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS)); + assertThat(session.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(300)); } @Test @@ -245,9 +255,10 @@ public class GemFireOperationsSessionRepositoryTests { InOrder inOrder = inOrder(sessionRepositorySpy); + inOrder.verify(sessionRepositorySpy, times(1)).configure(eq(actualSession)); + inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(actualSession)); inOrder.verify(sessionRepositorySpy, times(1)).commit(eq(actualSession)); inOrder.verify(sessionRepositorySpy, times(1)).touch(eq(actualSession)); - inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(actualSession)); } @Test @@ -337,9 +348,10 @@ public class GemFireOperationsSessionRepositoryTests { InOrder inOrder = inOrder(sessionRepositorySpy); + inOrder.verify(sessionRepositorySpy, times(1)).configure(eq(mockSession)); + inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSession)); inOrder.verify(sessionRepositorySpy, times(1)).commit(eq(mockSession)); inOrder.verify(sessionRepositorySpy, times(1)).touch(eq(mockSession)); - inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSession)); } @Test @@ -385,15 +397,18 @@ public class GemFireOperationsSessionRepositoryTests { InOrder inOrder = inOrder(sessionRepositorySpy); + inOrder.verify(sessionRepositorySpy, times(1)).configure(eq(mockSessionOne)); + inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionOne)); inOrder.verify(sessionRepositorySpy, times(1)).commit(eq(mockSessionOne)); inOrder.verify(sessionRepositorySpy, times(1)).touch(eq(mockSessionOne)); - inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionOne)); + inOrder.verify(sessionRepositorySpy, times(1)).configure(eq(mockSessionTwo)); + inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionTwo)); inOrder.verify(sessionRepositorySpy, times(1)).commit(eq(mockSessionTwo)); inOrder.verify(sessionRepositorySpy, times(1)).touch(eq(mockSessionTwo)); - inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionTwo)); + inOrder.verify(sessionRepositorySpy, times(1)).configure(eq(mockSessionThree)); + inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionThree)); inOrder.verify(sessionRepositorySpy, times(1)).commit(eq(mockSessionThree)); inOrder.verify(sessionRepositorySpy, times(1)).touch(eq(mockSessionThree)); - inOrder.verify(sessionRepositorySpy, times(1)).registerInterest(eq(mockSessionThree)); } @Test