diff --git a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerGemFireOperationsSessionRepositoryIntegrationTests.java b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerGemFireOperationsSessionRepositoryIntegrationTests.java index 5da5398..ca802ea 100644 --- a/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerGemFireOperationsSessionRepositoryIntegrationTests.java +++ b/spring-session-data-geode/src/integration-test/java/org/springframework/session/data/gemfire/ClientServerGemFireOperationsSessionRepositoryIntegrationTests.java @@ -89,6 +89,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte private static final int MAX_INACTIVE_INTERVAL_IN_SECONDS = 1; + private static final String GEMFIRE_LOG_LEVEL = "error"; private static final String TEST_SESSION_REGION_NAME = "TestClientServerSessions"; @Autowired @@ -125,7 +126,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte @Test public void createSessionFiresSessionCreatedEvent() { - Instant beforeOrAtCreationTime = Instant.now(); + Instant beforeCreationTime = Instant.now(); Session expectedSession = save(createSession()); @@ -135,14 +136,19 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte Session createdSession = sessionEvent.getSession(); + assertThat(createdSession).isNotNull(); assertThat(createdSession.getId()).isEqualTo(expectedSession.getId()); - assertThat(createdSession.getCreationTime().compareTo(beforeOrAtCreationTime)).isGreaterThanOrEqualTo(0); + assertThat(createdSession.getCreationTime()).isAfterOrEqualTo(beforeCreationTime); assertThat(createdSession.getLastAccessedTime()).isEqualTo(createdSession.getCreationTime()); assertThat(createdSession.getMaxInactiveInterval()).isEqualTo(Duration.ofSeconds(MAX_INACTIVE_INTERVAL_IN_SECONDS)); + assertThat(createdSession.getAttributeNames()).isEmpty(); - createdSession.setAttribute("attrOne", 1); + createdSession.setAttribute("attributeOne", 1); - assertThat(save(touch(createdSession)).getAttribute("attrOne")).isEqualTo(1); + assertThat(createdSession.getAttributeNames()).containsExactly("attributeOne"); + assertThat(createdSession.getAttribute("attributeOne")).isEqualTo(1); + + save(touch(createdSession)); sessionEvent = this.sessionEventListener.waitForSessionEvent(500); @@ -203,9 +209,9 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte } @ClientCacheApplication( - logLevel = "error", + logLevel = GEMFIRE_LOG_LEVEL, pingInterval = 5000, - readTimeout = 2500, + readTimeout = 2000, retryAttempts = 1, subscriptionEnabled = true ) @@ -226,7 +232,7 @@ public class ClientServerGemFireOperationsSessionRepositoryIntegrationTests exte @CacheServerApplication( name = "ClientServerGemFireOperationsSessionRepositoryIntegrationTests", - logLevel = "error" + logLevel = GEMFIRE_LOG_LEVEL ) @EnableGemFireHttpSession( regionName = TEST_SESSION_REGION_NAME, 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 6e064db..aac756c 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 @@ -41,7 +41,6 @@ import org.apache.geode.Delta; import org.apache.geode.InvalidDeltaException; import org.apache.geode.cache.EntryEvent; import org.apache.geode.cache.InterestResultPolicy; -import org.apache.geode.cache.Operation; import org.apache.geode.cache.Region; import org.apache.geode.cache.util.CacheListenerAdapter; @@ -68,7 +67,6 @@ import org.springframework.session.events.SessionDestroyedEvent; import org.springframework.session.events.SessionExpiredEvent; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -284,16 +282,6 @@ public abstract class AbstractGemFireOperationsSessionRepository return getSessionsRegion().getFullPath(); } - /** - * Determines whether {@link Region} {@literal register interest} is enabled - * in the current Apache Geode / Pivotal GemFire configuration. - * - * @return a boolean value indicating whether interest registration is enabled. - */ - protected boolean isRegisterInterestEnabled() { - return this.registerInterestEnabled; - } - /** * Return a reference to the {@link Log} used to log messages. * @@ -356,6 +344,16 @@ public abstract class AbstractGemFireOperationsSessionRepository .orElse(0); } + /** + * Determines whether {@link Region} {@literal register interest} is enabled + * in the current Apache Geode / Pivotal GemFire configuration. + * + * @return a boolean value indicating whether interest registration is enabled. + */ + protected boolean isRegisterInterestEnabled() { + return this.registerInterestEnabled; + } + protected Optional getSessionEventHandler() { return Optional.ofNullable(this.sessionEventHandler); } @@ -439,7 +437,7 @@ public abstract class AbstractGemFireOperationsSessionRepository * @see org.springframework.session.Session * @see #deleteById(String) */ - protected Session delete(@NonNull Session session) { + protected @Nullable Session delete(@NonNull Session session) { deleteById(session.getId()); @@ -451,14 +449,14 @@ public abstract class AbstractGemFireOperationsSessionRepository * * @param sessionId {@link String} containing the {@link Session#getId()} of the given {@link Session}. * @param session deleted {@link Session}. - * @see SessionEventHandlerCacheListenerAdapter#handleDeleted(String, Session) + * @see SessionEventHandlerCacheListenerAdapter#afterDelete(String, Session) * @see org.springframework.session.Session * @see #unregisterInterest(Object) */ protected void handleDeleted(String sessionId, Session session) { getSessionEventHandler() - .ifPresent(it -> it.handleDeleted(sessionId, session)); + .ifPresent(it -> it.afterDelete(sessionId, session)); unregisterInterest(sessionId); } @@ -1262,135 +1260,52 @@ public abstract class AbstractGemFireOperationsSessionRepository private final Set cachedSessionIds = new ConcurrentSkipListSet<>(); - protected SessionEventHandlerCacheListenerAdapter(AbstractGemFireOperationsSessionRepository sessionRepository) { + /** + * Constructs a new instance of the {@link SessionEventHandlerCacheListenerAdapter} initialized with + * the given {@link AbstractGemFireOperationsSessionRepository}. + * + * @param sessionRepository {@link AbstractGemFireOperationsSessionRepository} used by this event handler + * to manage {@link AbstractSessionEvent Session Events}. + * @throws IllegalArgumentException if {@link AbstractGemFireOperationsSessionRepository} is {@literal null}. + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository + */ + protected SessionEventHandlerCacheListenerAdapter( + AbstractGemFireOperationsSessionRepository sessionRepository) { Assert.notNull(sessionRepository, "SessionRepository is required"); this.sessionRepository = sessionRepository; } - protected AbstractGemFireOperationsSessionRepository getSessionRepository() { + /** + * Returns a reference to the configured {@link SessionRepository}. + * + * @return a reference to the configured {@link SessionRepository}. + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository + */ + protected @NonNull AbstractGemFireOperationsSessionRepository getSessionRepository() { return this.sessionRepository; } /** - * Callback method triggered when an entry is created in the Pivotal GemFire cache {@link Region}. + * Callback method triggered when an entry is created (put) in the {@link Session} cache {@link Region}. * * @param event {@link EntryEvent} containing the details of the cache operation. + * @see org.springframework.session.events.SessionCreatedEvent + * @see org.springframework.session.Session * @see org.apache.geode.cache.EntryEvent - * @see #handleCreated(String, Session) + * @see #newSessionCreatedEvent(Session) + * @see #publishEvent(ApplicationEvent) + * @see #toSession(Object, Object) + * @see #forget(Object) */ @Override public void afterCreate(EntryEvent event) { Optional.ofNullable(event) - .filter(this::isCreate) - .ifPresent(it -> { - - String sessionId = it.getKey().toString(); - - handleCreated(sessionId, toSession(it.getNewValue(), sessionId)); - }); - } - - /** - * Causes Session created events to be published to the Spring application context. - * - * @param sessionId a String indicating the ID of the Session. - * @param session a reference to the Session triggering the event. - * @see org.springframework.session.events.SessionCreatedEvent - * @see org.springframework.session.Session - * @see #newSessionCreatedEvent(Session) - * @see #publishEvent(ApplicationEvent) - */ - protected void handleCreated(String sessionId, Session session) { - - remember(sessionId); - getSessionRepository().publishEvent(newSessionCreatedEvent(session)); - } - - private SessionCreatedEvent newSessionCreatedEvent(Session session) { - return new SessionCreatedEvent(getSessionRepository(), session); - } - - /** - * Callback method triggered when an entry is destroyed in the Pivotal GemFire cache {@link Region}. - * - * @param event {@link EntryEvent} containing the details of the cache operation. - * @see org.apache.geode.cache.EntryEvent - * @see #handleDestroyed(String, Session) - */ - @Override - public void afterDestroy(EntryEvent event) { - - Optional.ofNullable(event) - .ifPresent(it -> { - - String sessionId = event.getKey().toString(); - - handleDestroyed(sessionId, toSession(event.getOldValue(), sessionId)); - }); - } - - /** - * Causes Session destroyed events to be published to the Spring application context. - * - * @param sessionId a String indicating the ID of the Session. - * @param session a reference to the Session triggering the event. - * @see org.springframework.session.events.SessionDestroyedEvent - * @see org.springframework.session.Session - * @see #newSessionDestroyedEvent(Session) - * @see #publishEvent(ApplicationEvent) - * @see #forget(Object) - */ - protected void handleDestroyed(String sessionId, Session session) { - - forget(sessionId); - getSessionRepository().publishEvent(newSessionDestroyedEvent(session)); - } - - private SessionDestroyedEvent newSessionDestroyedEvent(Session session) { - return new SessionDestroyedEvent(getSessionRepository(), session); - } - - /** - * Callback method triggered when an entry is invalidated in the Pivotal GemFire cache {@link Region}. - * - * @param event {@link EntryEvent} containing the details of the cache operation. - * @see org.apache.geode.cache.EntryEvent - * @see #handleExpired(String, Session) - */ - @Override - public void afterInvalidate(EntryEvent event) { - - Optional.ofNullable(event) - .ifPresent(it -> { - - String sessionId = event.getKey().toString(); - - handleExpired(sessionId, toSession(event.getOldValue(), sessionId)); - }); - } - - /** - * Causes Session expired events to be published to the Spring application context. - * - * @param sessionId a String indicating the ID of the Session. - * @param session a reference to the Session triggering the event. - * @see org.springframework.session.events.SessionExpiredEvent - * @see org.springframework.session.Session - * @see #newSessionExpiredEvent(Session) - * @see #publishEvent(ApplicationEvent) - * @see #forget(Object) - */ - protected void handleExpired(String sessionId, Session session) { - - forget(sessionId); - getSessionRepository().publishEvent(newSessionExpiredEvent(session)); - } - - private SessionExpiredEvent newSessionExpiredEvent(Session session) { - return new SessionExpiredEvent(getSessionRepository(), session); + .filter(this::remember) + .ifPresent(it -> getSessionRepository() + .publishEvent(newSessionCreatedEvent(toSession(it.getNewValue(), it.getKey())))); } /** @@ -1402,73 +1317,224 @@ public abstract class AbstractGemFireOperationsSessionRepository * @see org.springframework.session.Session * @see #newSessionDeletedEvent(Session) * @see #publishEvent(ApplicationEvent) + * @see #toSession(Object, Object) * @see #forget(Object) */ - protected void handleDeleted(String sessionId, Session session) { + protected void afterDelete(String sessionId, Session session) { forget(sessionId); getSessionRepository().publishEvent(newSessionDeletedEvent(toSession(session, sessionId))); } - private SessionDeletedEvent newSessionDeletedEvent(Session session) { - return new SessionDeletedEvent(getSessionRepository(), session); - } + /** + * Callback method triggered when an entry is destroyed (removed) in the {@link Session} cache {@link Region}. + * + * @param event {@link EntryEvent} containing the details of the cache operation. + * @see org.springframework.session.events.SessionDestroyedEvent + * @see org.springframework.session.Session + * @see org.apache.geode.cache.EntryEvent + * @see #newSessionDestroyedEvent(Session) + * @see #publishEvent(ApplicationEvent) + * @see #toSession(Object, Object) + * @see #forget(Object) + */ + @Override + public void afterDestroy(EntryEvent event) { - boolean isCreate(EntryEvent event) { - return isCreate(event.getOperation()) && isNotUpdate(event) && isSession(event.getNewValue()); - } - - private boolean isCreate(Operation operation) { - return operation.isCreate() && !Operation.LOCAL_LOAD_CREATE.equals(operation); - } - - private boolean isNotUpdate(EntryEvent event) { - return isNotProxyRegion() || !this.cachedSessionIds.contains(ObjectUtils.nullSafeHashCode(event.getKey())); - } - - private boolean isNotProxyRegion() { - return !isProxyRegion(); - } - - private boolean isProxyRegion() { - return GemFireUtils.isProxy(getSessionRepository().getSessionsRegion()); + Optional.ofNullable(event) + .filter(this::forget) + .ifPresent(it -> getSessionRepository() + .publishEvent(newSessionDestroyedEvent(toSession(event.getOldValue(), it.getKey())))); } /** - * Used to determine whether the application developer is storing (HTTP) {@link Session Sessions} with other, - * arbitrary application domain objects in the same Apache Geode / Pivotal GemFire cache {@link Region}; - * crazier things have happened! + * Callback method triggered when an entry is invalidated (expired) in the {@link Session} cache {@link Region}. * - * @param obj {@link Object} to evaluate. - * @return a boolean value indicating whether the old/new {@link Object} from the {@link Region} - * {@link EntryEvent} is indeed a {@link Session}. + * @param event {@link EntryEvent} containing the details of the cache operation. + * @see org.springframework.session.events.SessionExpiredEvent * @see org.springframework.session.Session + * @see org.apache.geode.cache.EntryEvent + * @see #newSessionExpiredEvent(Session) + * @see #publishEvent(ApplicationEvent) + * @see #toSession(Object, Object) + * @see #forget(Object) */ - private boolean isSession(Object obj) { - return obj instanceof Session; + @Override + public void afterInvalidate(EntryEvent event) { + + Optional.ofNullable(event) + .filter(this::forget) + .ifPresent(it -> getSessionRepository() + .publishEvent(newSessionExpiredEvent(toSession(event.getOldValue(), it.getKey())))); + } + + /** + * Constructs a new {@link SessionCreatedEvent} initialized with the given {@link Session}, + * using the {@link #getSessionRepository() SessionRepository} as the event source. + * + * @param session {@link Session} that is the subject of the {@link AbstractSessionEvent event}. + * @return a new {@link SessionCreatedEvent}. + * @see org.springframework.session.events.SessionCreatedEvent + * @see org.springframework.session.Session + * @see #getSessionRepository() + */ + protected SessionCreatedEvent newSessionCreatedEvent(Session session) { + return new SessionCreatedEvent(getSessionRepository(), session); + } + + /** + * Constructs a new {@link SessionDeletedEvent} initialized with the given {@link Session}, + * using the {@link #getSessionRepository() SessionRepository} as the event source. + * + * @param session {@link Session} that is the subject of the {@link AbstractSessionEvent event}. + * @return a new {@link SessionDeletedEvent}. + * @see org.springframework.session.events.SessionDeletedEvent + * @see org.springframework.session.Session + * @see #getSessionRepository() + */ + protected SessionDeletedEvent newSessionDeletedEvent(Session session) { + return new SessionDeletedEvent(getSessionRepository(), session); + } + + /** + * Constructs a new {@link SessionDestroyedEvent} initialized with the given {@link Session}, + * using the {@link #getSessionRepository() SessionRepository} as the event source. + * + * @param session {@link Session} that is the subject of the {@link AbstractSessionEvent event}. + * @return a new {@link SessionDestroyedEvent}. + * @see org.springframework.session.events.SessionDestroyedEvent + * @see org.springframework.session.Session + * @see #getSessionRepository() + */ + protected SessionDestroyedEvent newSessionDestroyedEvent(Session session) { + return new SessionDestroyedEvent(getSessionRepository(), session); + } + + /** + * Constructs a new {@link SessionExpiredEvent} initialized with the given {@link Session}, + * using the {@link #getSessionRepository() SessionRepository} as the event source. + * + * @param session {@link Session} that is the subject of the {@link AbstractSessionEvent event}. + * @return a new {@link SessionExpiredEvent}. + * @see org.springframework.session.events.SessionExpiredEvent + * @see org.springframework.session.Session + * @see #getSessionRepository() + */ + protected SessionExpiredEvent newSessionExpiredEvent(Session session) { + return new SessionExpiredEvent(getSessionRepository(), session); + } + + Set getCachedSessionIds() { + return this.cachedSessionIds; + } + + /** + * Determines whether the given {@link Session#getId() Session ID} has been remembered. + * + * @param sessionId {@link Object Session ID} to evaluate. + * @return return a boolean value determining whether the given {@link Session#getId() Session ID} + * has been remembered. + * @see #getCachedSessionIds() + */ + protected boolean isRemembered(Object sessionId) { + return getCachedSessionIds().contains(ObjectUtils.nullSafeHashCode(sessionId)); + } + + /** + * Forgets the {@link EntryEvent#getKey() Key} contained in the given {@link EntryEvent} + * as a {@link Session#getId() Session ID}. + * + * @param entryEvent {@link EntryEvent} to evaluate. + * @return {@literal true} if the {@link EntryEvent#getKey() Key} contained in the given {@link EntryEvent} + * was forgotten as a {@link Session#getId() Session ID}. + * @see org.springframework.session.Session + * @see org.apache.geode.cache.EntryEvent + * @see #forget(Object) + */ + protected boolean forget(EntryEvent entryEvent) { + + return Optional.ofNullable(entryEvent) + .map(EntryEvent::getKey) + .map(this::forget) + .orElse(false); } /** * Forgets the given {@link Object Session ID}. * - * @param sessionId {@link Object} containing the Session ID to forget. - * @return a boolean value indicating whether the given Session ID was even being remembered. + * @param sessionId {@link Object} containing the {@link Session#getId() Session ID} to forget. + * @return a boolean value indicating whether the given {@link Session#getId() Session ID} was forgotten. + * @see #getCachedSessionIds() * @see #remember(Object) */ - boolean forget(Object sessionId) { - return this.cachedSessionIds.remove(ObjectUtils.nullSafeHashCode(sessionId)); + protected boolean forget(Object sessionId) { + return getCachedSessionIds().remove(ObjectUtils.nullSafeHashCode(sessionId)); + } + + /** + * Remembers the {@link EntryEvent#getKey() Key} contained by the given {@link EntryEvent} + * iff the {@link EntryEvent#getKey() Key} is a valid {@link Session#getId() Session ID} + * and the {@link EntryEvent#getNewValue() new value} is a {@link Session}. + * + * @param entryEvent {@link EntryEvent} to evaluate. + * @return {@literal true} if the {@link EntryEvent#getKey() Key} of the given {@link EntryEvent} + * is a valid {@link Session#getId() Session ID}. + * @see SessionUtils#isValidSessionId(Object) + * @see #isSession(EntryEvent) + * @see #remember(Object) + * @see org.springframework.session.Session + * @see org.apache.geode.cache.EntryEvent + */ + protected boolean remember(EntryEvent entryEvent) { + + return Optional.ofNullable(entryEvent) + .filter(this::isSession) + .map(EntryEvent::getKey) + .filter(SessionUtils::isValidSessionId) + .map(this::remember) + .orElse(false); } /** * Remembers the given {@link Object Session ID}. * - * @param sessionId {@link Object} containing the Session ID to remember. - * @return a boolean value indicating whether Spring Session is interested in - * and will remember the given Session ID. + * @param sessionId {@link Object} containing the {@link Session#getId() Session ID} to remember. + * @return a boolean value indicating whether Spring Session is interested in and will remember + * the given {@link Session#getId() Session ID}. + * @see #getCachedSessionIds() * @see #forget(Object) */ - boolean remember(Object sessionId) { - return isProxyRegion() && this.cachedSessionIds.add(ObjectUtils.nullSafeHashCode(sessionId)); + protected boolean remember(Object sessionId) { + return getCachedSessionIds().add(ObjectUtils.nullSafeHashCode(sessionId)); + } + + /** + * Determines whether the {@link EntryEvent#getNewValue() new value} contained in the {@link EntryEvent} + * is a {@link Session}. + * + * @param entryEvent {@link EntryEvent} to evaluate. + * @return a boolean value indicating whether the {@link EntryEvent#getNewValue() new value} + * contained in the {@link EntryEvent} is a {@link Session}. + * @see org.springframework.session.Session + * @see org.apache.geode.cache.EntryEvent + */ + protected boolean isSession(EntryEvent entryEvent) { + + return Optional.ofNullable(entryEvent) + .map(EntryEvent::getNewValue) + .filter(Session.class::isInstance) + .isPresent(); + } + + /** + * Determines whether the given {@link Object} is a {@link Session}. + * + * @param target {@link Object} to evaluate. + * @return a boolean value determining whether the given {@link Object} is a {@link Session}. + * @see org.springframework.session.Session + */ + protected boolean isSession(Object target) { + return target instanceof Session; } /** @@ -1477,22 +1543,25 @@ public abstract class AbstractGemFireOperationsSessionRepository * Otherwise, this method attempts to use the supplied {@link String Session ID} to create a {@link Session} * representation containing only the ID. * - * @param obj {@link Object} to evaluate as a {@link Session}. - * @param sessionId {@link String} containing the Session ID. + * @param target {@link Object} to evaluate as a {@link Session}. + * @param sessionId {@link String} containing the {@link Session#getId() Session ID}. * @return a {@link Session} from the given {@link Object} or a {@link Session} representation * containing only the supplied {@link String Session ID}. * @throws IllegalStateException if the given {@link Object} is not a {@link Session} * and a {@link String Session ID} was not supplied. + * @see org.springframework.session.Session + * @see SessionUtils#isValidSessionId(Object) + * @see #isSession(Object) */ - Session toSession(@Nullable Object obj, String sessionId) { + protected Session toSession(@Nullable Object target, Object sessionId) { - return obj instanceof Session - ? (Session) obj + return isSession(target) ? (Session) target : Optional.ofNullable(sessionId) - .filter(StringUtils::hasText) - .map(SessionIdHolder::create) - .orElseThrow(() -> newIllegalStateException( - "Minimally, the Session ID [%s] must be known to trigger a Session event", sessionId)); + .filter(SessionUtils::isValidSessionId) + .map(Object::toString) + .map(SessionIdHolder::create) + .orElseThrow(() -> newIllegalStateException( + "Session or the Session ID [%s] must be known to trigger a Session event", sessionId)); } } @@ -1500,6 +1569,15 @@ public abstract class AbstractGemFireOperationsSessionRepository private final AbstractGemFireOperationsSessionRepository sessionRepository; + /** + * Constructs a new instance of the {@link SessionIdInterestRegisteringCacheListener} initialized with + * the {@link AbstractGemFireOperationsSessionRepository}. + * + * @param sessionRepository {@link AbstractGemFireOperationsSessionRepository} used by this listener + * to register and unregister interests in {@link Session Sessions}. + * @throws IllegalArgumentException if {@link AbstractGemFireOperationsSessionRepository} is {@literal null}. + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository + */ public SessionIdInterestRegisteringCacheListener(AbstractGemFireOperationsSessionRepository sessionRepository) { Assert.notNull(sessionRepository, "SessionRepository is required"); @@ -1507,6 +1585,12 @@ public abstract class AbstractGemFireOperationsSessionRepository this.sessionRepository = sessionRepository; } + /** + * Returns a reference to the configured {@link SessionRepository}. + * + * @return a reference to the configured {@link SessionRepository}. + * @see org.springframework.session.data.gemfire.AbstractGemFireOperationsSessionRepository + */ protected AbstractGemFireOperationsSessionRepository getSessionRepository() { return this.sessionRepository; } 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 fed8ad6..c881e0e 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 @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; @@ -58,20 +59,18 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import edu.umd.cs.mtc.MultithreadedTestCase; import edu.umd.cs.mtc.TestFramework; import org.apache.geode.cache.AttributesMutator; -import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.EntryEvent; import org.apache.geode.cache.InterestResultPolicy; import org.apache.geode.cache.Operation; @@ -88,11 +87,13 @@ 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.GemFireOperationsSessionRepositorySupport; +import org.springframework.session.data.gemfire.support.SessionIdHolder; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; import org.springframework.session.events.SessionDestroyedEvent; import org.springframework.session.events.SessionExpiredEvent; +import org.springframework.util.ObjectUtils; import org.apache.commons.logging.Log; @@ -139,22 +140,18 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @SuppressWarnings("all") public void setup() { - AttributesMutator mockAttributesMutator = mock(AttributesMutator.class); - - when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator); - - GemfireTemplate gemfireTemplate = new GemfireTemplate(this.mockRegion); - - this.sessionRepository = spy(new TestGemFireOperationsSessionRepository(gemfireTemplate)); + this.sessionRepository = new TestGemFireOperationsSessionRepository(new GemfireTemplate(this.mockRegion)); this.sessionRepository.setUseDataSerialization(false); + this.sessionRepository = spy(this.sessionRepository); doReturn(this.mockLog).when(this.sessionRepository).getLogger(); + doReturn(this.mockRegion).when(this.sessionRepository).getSessionsRegion(); } @SuppressWarnings("unchecked") private EntryEvent mockEntryEvent(Operation operation, K key, V oldValue, V newValue) { - EntryEvent mockEntryEvent = mock(EntryEvent.class); + EntryEvent mockEntryEvent = mock(EntryEvent.class, withSettings().lenient()); when(mockEntryEvent.getOperation()).thenReturn(operation); when(mockEntryEvent.getKey()).thenReturn(key); @@ -164,24 +161,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { return mockEntryEvent; } - @SuppressWarnings("unchecked") - private Region mockRegion(String name, DataPolicy dataPolicy) { - - Region mockRegion = mock(Region.class, name); - - RegionAttributes mockRegionAttributes = mockRegionAttributes(name); - - when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); - when(mockRegionAttributes.getDataPolicy()).thenReturn(dataPolicy); - - return mockRegion; - } - - @SuppressWarnings("unchecked") - private RegionAttributes mockRegionAttributes(String name) { - return mock(RegionAttributes.class, name); - } - private Session mockSession() { String sessionId = UUID.randomUUID().toString(); @@ -231,14 +210,6 @@ public class AbstractGemFireOperationsSessionRepositoryTests { return session; } - private AbstractGemFireOperationsSessionRepository withRegion( - AbstractGemFireOperationsSessionRepository sessionRepository, Region region) { - - doReturn(region).when(sessionRepository).getSessionsRegion(); - - return sessionRepository; - } - @Test @SuppressWarnings("unchecked") public void constructGemFireOperationsSessionRepository() throws Exception { @@ -257,7 +228,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator); when(mockRegion.getFullPath()).thenReturn(RegionUtils.toRegionPath("Example")); when(mockRegion.getRegionService()).thenReturn(mockClientCache); - when(mockRegionAttributes.getPoolName()).thenReturn("Car"); + when(mockRegionAttributes.getPoolName()).thenReturn("Dead"); GemfireTemplate template = new GemfireTemplate(mockRegion); @@ -270,6 +241,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { .isEqualTo(RegionUtils.toRegionPath("Example")); assertThat(sessionRepository.getMaxInactiveIntervalInSeconds()) .isEqualTo(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS); + assertThat(sessionRepository.isRegisterInterestEnabled()).isTrue(); assertThat(sessionRepository.getSessionEventHandler().orElse(null)) .isInstanceOf(SessionEventHandlerCacheListenerAdapter.class); assertThat(sessionRepository.getSessionsRegion()).isSameAs(mockRegion); @@ -353,6 +325,17 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } } + @Test + public void getFullyQualifiedRegionNameUsesRegionFullPath() { + + when(this.mockRegion.getFullPath()).thenReturn("/Region/Full/Path"); + + assertThat(this.sessionRepository.getFullyQualifiedRegionName()).isEqualTo("/Region/Full/Path"); + + verify(this.sessionRepository, times(1)).getSessionsRegion(); + verify(this.mockRegion, times(1)).getFullPath(); + } + @Test public void setAndGetMaxInactiveInterval() { @@ -364,16 +347,32 @@ public class AbstractGemFireOperationsSessionRepositoryTests { this.sessionRepository.setMaxInactiveInterval(tenMinutes); assertThat(this.sessionRepository.getMaxInactiveInterval()).isEqualTo(tenMinutes); + assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(600); + } - this.sessionRepository.setMaxInactiveIntervalInSeconds(300); + @Test + public void setMaxInactiveIntervalToNull() { - assertThat(this.sessionRepository.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(5)); + assertThat(this.sessionRepository.getMaxInactiveInterval()) + .isEqualTo(Duration.ofSeconds(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS)); this.sessionRepository.setMaxInactiveInterval(null); assertThat(this.sessionRepository.getMaxInactiveInterval()).isNull(); } + @Test + public void setMaxInactiveIntervalUsingSeconds() { + + assertThat(this.sessionRepository.getMaxInactiveInterval()) + .isEqualTo(Duration.ofSeconds(GemFireHttpSessionConfiguration.DEFAULT_MAX_INACTIVE_INTERVAL_IN_SECONDS)); + + this.sessionRepository.setMaxInactiveIntervalInSeconds(300); + + assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(300); + assertThat(this.sessionRepository.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(5)); + } + @Test public void maxInactiveIntervalInSecondsAllowsExtremelyLargeAndNegativeValues() { @@ -397,6 +396,43 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(this.sessionRepository.getMaxInactiveIntervalInSeconds()).isEqualTo(Integer.MAX_VALUE); } + @Test + @SuppressWarnings("unchecked") + public void isRegisterInterestEnabledReturnsTrue() { + + AttributesMutator mockAttributesMutator = mock(AttributesMutator.class); + + ClientCache mockClientCache = mock(ClientCache.class); + + Region mockRegion = mock(Region.class); + + RegionAttributes mockRegionAttributes = mock(RegionAttributes.class); + + when(mockRegion.getAttributes()).thenReturn(mockRegionAttributes); + when(mockRegion.getAttributesMutator()).thenReturn(mockAttributesMutator); + when(mockRegion.getRegionService()).thenReturn(mockClientCache); + when(mockRegionAttributes.getPoolName()).thenReturn("Dead"); + + AbstractGemFireOperationsSessionRepository sessionRepository = + new TestGemFireOperationsSessionRepository(new GemfireTemplate(mockRegion)); + + assertThat(sessionRepository).isNotNull(); + assertThat(sessionRepository.isRegisterInterestEnabled()).isTrue(); + } + + @Test + @SuppressWarnings("unchecked") + public void isRegisterInterestEnabledReturnsFalse() { + + Region mockRegion = mock(Region.class); + + AbstractGemFireOperationsSessionRepository sessionRepository = + new TestGemFireOperationsSessionRepository(new GemfireTemplate(mockRegion)); + + assertThat(sessionRepository).isNotNull(); + assertThat(sessionRepository.isRegisterInterestEnabled()).isFalse(); + } + @Test public void setAndIsUsingDataSerialization() { @@ -452,9 +488,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test public void handleDeletedSessionForgetsSessionIdPublishesSessionDeletedEventAndUnregistersInterest() { - String sessionId = "12345"; - - when(this.mockSession.getId()).thenReturn(sessionId); + when(this.mockSession.getId()).thenReturn("12345"); ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); @@ -467,7 +501,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo(sessionId); + assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); return null; @@ -477,37 +511,37 @@ public class AbstractGemFireOperationsSessionRepositoryTests { SessionEventHandlerCacheListenerAdapter mockSessionEventHandler = mock(SessionEventHandlerCacheListenerAdapter.class); - doReturn(this.sessionRepository).when(mockSessionEventHandler).getSessionRepository(); - doCallRealMethod().when(mockSessionEventHandler).handleDeleted(anyString(), any(Session.class)); - doCallRealMethod().when(mockSessionEventHandler).toSession(any(), anyString()); doReturn(mockApplicationEventPublisher).when(this.sessionRepository).getApplicationEventPublisher(); doReturn(Optional.of(mockSessionEventHandler)).when(this.sessionRepository).getSessionEventHandler(); + doReturn(this.sessionRepository).when(mockSessionEventHandler).getSessionRepository(); + doCallRealMethod().when(mockSessionEventHandler).afterDelete(anyString(), any(Session.class)); + doCallRealMethod().when(mockSessionEventHandler).newSessionDeletedEvent(any(Session.class)); + doCallRealMethod().when(mockSessionEventHandler).toSession(any(), anyString()); - this.sessionRepository.handleDeleted(sessionId, this.mockSession); + this.sessionRepository.handleDeleted("12345", this.mockSession); - verify(mockSessionEventHandler, times(1)).handleDeleted(eq(sessionId), eq(this.mockSession)); - verify(mockSessionEventHandler, times(1)).forget(eq(sessionId)); + verify(mockSessionEventHandler, times(1)) + .afterDelete(eq("12345"), eq(this.mockSession)); + verify(mockSessionEventHandler, times(1)).forget(eq("12345")); verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDeletedEvent.class)); - verify(this.sessionRepository, times(1)).unregisterInterest(eq(sessionId)); - verify(this.mockSession, times(1)).getId(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); + verify(this.sessionRepository, times(1)).unregisterInterest(eq("12345")); verify(mockApplicationEventPublisher, times(1)) .publishEvent(isA(SessionDeletedEvent.class)); + verify(this.mockSession, times(1)).getId(); + verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); } @Test public void handleDeletedSessionWhenNoSessionEventHandlerIsPresentDoesNotPublishEventButStillUnregistersInterest() { - Session mockSession = mock(Session.class); - doReturn(Optional.empty()).when(this.sessionRepository).getSessionEventHandler(); - this.sessionRepository.handleDeleted("1", mockSession); + this.sessionRepository.handleDeleted("1", this.mockSession); verify(this.sessionRepository, times(1)).getSessionEventHandler(); verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); verify(this.sessionRepository, times(1)).unregisterInterest(eq("1")); - verifyZeroInteractions(mockSession); + verifyZeroInteractions(this.mockSession); } @Test @@ -517,9 +551,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + doReturn(mockApplicationEventPublisher).when(this.sessionRepository).getApplicationEventPublisher(); this.sessionRepository.publishEvent(mockApplicationEvent); @@ -536,48 +568,74 @@ public class AbstractGemFireOperationsSessionRepositoryTests { doThrow(new IllegalStateException("test")).when(mockApplicationEventPublisher) .publishEvent(any(ApplicationEvent.class)); - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); + doReturn(mockApplicationEventPublisher).when(this.sessionRepository).getApplicationEventPublisher(); this.sessionRepository.publishEvent(mockApplicationEvent); verify(mockApplicationEventPublisher, times(1)).publishEvent(eq(mockApplicationEvent)); + verify(this.mockLog, times(1)) .error(eq(String.format("Error occurred while publishing event [%s]", mockApplicationEvent)), isA(IllegalStateException.class)); } + private Session testRegisterInterestWithInvalidSession(Session session) { + + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); + + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); + + Session returnedSession = this.sessionRepository.registerInterest(session); + + verifyNoRegionRegisterInterestCalls(this.mockRegion); + + return returnedSession; + } + + private void verifyNoRegionRegisterInterestCalls(Region region) { + + verify(region, never()).registerInterest(any()); + verify(region, never()).registerInterest(any(), anyBoolean()); + verify(region, never()).registerInterest(any(), anyBoolean(), anyBoolean()); + verify(region, never()).registerInterest(any(), any(InterestResultPolicy.class)); + verify(region, never()).registerInterest(any(), any(InterestResultPolicy.class), anyBoolean()); + verify(region, never()).registerInterest(any(), any(InterestResultPolicy.class), anyBoolean(), anyBoolean()); + } + @Test public void registerInterestIsNullSafe() { assertThat(testRegisterInterestWithInvalidSession(null)).isNull(); } @Test - public void registerInterestWithSession() { + public void registerInterestWhenRegisteringInterestIsNotEnabled() { when(this.mockSession.getId()).thenReturn("1"); - when(this.sessionRepository.isRegisterInterestEnabled()).thenReturn(true); + doReturn(false).when(this.sessionRepository).isRegisterInterestEnabled(); + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); assertThat(this.sessionRepository.registerInterest(this.mockSession)).isSameAs(this.mockSession); verify(this.mockSession, times(1)).getId(); - verify(this.mockRegion, times(1)) - .registerInterest(eq("1"), eq(InterestResultPolicy.NONE), eq(false), eq(false)); + verify(this.sessionRepository, times(1)).registerInterest(eq("1")); + verify(this.sessionRepository, times(1)).isRegisterInterestEnabled(); + verifyNoRegionRegisterInterestCalls(this.mockRegion); } - private Session testRegisterInterestWithInvalidSession(Session session) { + @Test + public void registerInterestWithSession() { - Session returnedSession = this.sessionRepository.registerInterest(session); + when(this.mockSession.getId()).thenReturn("1"); + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); - verify(this.mockRegion, never()).registerInterest(any()); - verify(this.mockRegion, never()).registerInterest(any(), anyBoolean()); - verify(this.mockRegion, never()).registerInterest(any(), anyBoolean(), anyBoolean()); - verify(this.mockRegion, never()).registerInterest(any(), any(InterestResultPolicy.class)); - verify(this.mockRegion, never()).registerInterest(any(), any(InterestResultPolicy.class), anyBoolean()); - verify(this.mockRegion, never()).registerInterest(any(), any(InterestResultPolicy.class), anyBoolean(), anyBoolean()); + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); + assertThat(this.sessionRepository.registerInterest(this.mockSession)).isSameAs(this.mockSession); - return returnedSession; + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)).registerInterest(eq("1")); + verify(this.sessionRepository, times(1)).isRegisterInterestEnabled(); + verify(this.mockRegion, times(1)) + .registerInterest(eq("1"), eq(InterestResultPolicy.NONE), eq(false), eq(false)); } @Test @@ -608,12 +666,15 @@ public class AbstractGemFireOperationsSessionRepositoryTests { public void registerInterestWithTheSameSessionTwice() { when(this.mockSession.getId()).thenReturn("1"); - when(this.sessionRepository.isRegisterInterestEnabled()).thenReturn(true); + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); assertThat(this.sessionRepository.registerInterest(this.mockSession)).isEqualTo(this.mockSession); assertThat(this.sessionRepository.registerInterest(this.mockSession)).isEqualTo(this.mockSession); + verify(this.mockSession, times(2)).getId(); verify(this.sessionRepository, times(2)).registerInterest(eq("1")); + verify(this.sessionRepository, times(2)).isRegisterInterestEnabled(); verify(this.mockRegion, times(1)) .registerInterest(eq("1"), eq(InterestResultPolicy.NONE), eq(false), eq(false)); } @@ -631,16 +692,51 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(this.sessionRepository.unregisterInterest(null)).isNull(); } + @Test + public void unregisterInterestWhenRegisteringInterestIsDisabled() { + + when(this.mockSession.getId()).thenReturn("1"); + doReturn(false).when(this.sessionRepository).isRegisterInterestEnabled(); + + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); + assertThat(this.sessionRepository.unregisterInterest(this.mockSession)).isSameAs(this.mockSession); + + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)).unregisterInterest(eq("1")); + verify(this.sessionRepository, times(1)).isRegisterInterestEnabled(); + verify(this.mockRegion, never()).unregisterInterest(any()); + } + @Test public void unregisterInterestWithRegisteredSession() { when(this.mockSession.getId()).thenReturn("1"); - when(this.sessionRepository.isRegisterInterestEnabled()).thenReturn(true); + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); assertThat(this.sessionRepository.registerInterest(this.mockSession)).isSameAs(this.mockSession); assertThat(this.sessionRepository.unregisterInterest(this.mockSession)).isSameAs(this.mockSession); verify(this.mockSession, times(2)).getId(); + verify(this.sessionRepository, times(1)).unregisterInterest(eq("1")); + verify(this.sessionRepository, times(2)).isRegisterInterestEnabled(); + verify(this.mockRegion, times(1)).unregisterInterest(eq("1")); + } + + @Test + public void unregisterInterestWithTheSameSessionTwice() { + + when(this.mockSession.getId()).thenReturn("1"); + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); + + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); + assertThat(this.sessionRepository.registerInterest(this.mockSession)).isSameAs(this.mockSession); + assertThat(this.sessionRepository.unregisterInterest(this.mockSession)).isSameAs(this.mockSession); + assertThat(this.sessionRepository.unregisterInterest(this.mockSession)).isSameAs(this.mockSession); + + verify(this.mockSession, times(3)).getId(); + verify(this.sessionRepository, times(2)).unregisterInterest(eq("1")); + verify(this.sessionRepository, times(3)).isRegisterInterestEnabled(); verify(this.mockRegion, times(1)).unregisterInterest(eq("1")); } @@ -648,168 +744,919 @@ public class AbstractGemFireOperationsSessionRepositoryTests { public void unregisterInterestWithUnknownSession() { when(this.mockSession.getId()).thenReturn("1"); + doReturn(true).when(this.sessionRepository).isRegisterInterestEnabled(); + assertThat(this.sessionRepository.getSessionsRegion()).isEqualTo(this.mockRegion); assertThat(this.sessionRepository.unregisterInterest(this.mockSession)).isSameAs(this.mockSession); verify(this.mockSession, times(1)).getId(); verify(this.sessionRepository, times(1)).unregisterInterest(eq("1")); + verify(this.sessionRepository, times(1)).isRegisterInterestEnabled(); verify(this.mockRegion, never()).unregisterInterest(any()); } @Test - public void isCreateWithCreateOperationReturnsTrue() { + public void constructSessionEventHandlerCacheListenerAdapter() { - SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + AbstractGemFireOperationsSessionRepository mockSessionRepository = + mock(AbstractGemFireOperationsSessionRepository.class); - EntryEvent mockEntryEvent = - mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); + SessionEventHandlerCacheListenerAdapter sessionEventHanlder = + new SessionEventHandlerCacheListenerAdapter(mockSessionRepository); - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + assertThat(sessionEventHanlder).isNotNull(); + assertThat(sessionEventHanlder.getSessionRepository()).isSameAs(mockSessionRepository); + } - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isTrue(); + @Test(expected = IllegalArgumentException.class) + public void constructSessionEventHandlerCacheListenerAdapterWithNull() { - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, times(1)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(this.mockSession); + try { + new SessionEventHandlerCacheListenerAdapter(null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("SessionRepository is required"); + assertThat(expected).hasNoCause(); + + throw expected; + } } @Test - public void isCreateWithCreateOperationAndNonProxyRegionReturnsTrue() { + public void newSessionEventHandlerCacheListenerAdapterUsingSessionRepository() { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.getSessionRepository()).isSameAs(this.sessionRepository); + } - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.NORMAL)); + @Test + public void afterCreateIsNullSafe() { - sessionEventHandler.remember("12345"); + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isTrue(); + sessionEventHandler.afterCreate(null); + + verify(sessionEventHandler, never()).remember(any()); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionCreatedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterCreateHandlesNewSessionPublishesSessionCreatedEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession); + when(this.mockSession.getId()).thenReturn("1"); + doNothing().when(this.sessionRepository).publishEvent(any(ApplicationEvent.class)); + + sessionEventHandler.afterCreate(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(2)).getNewValue(); + verify(sessionEventHandler, times(1)).remember(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionCreatedEvent(eq(this.mockSession)); + verify(sessionEventHandler, times(1)).toSession(eq(this.mockSession), eq("1")); + verify(this.mockSession, times(1)).getId(); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionCreatedEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterCreateHandlesKnownSessionWillNotPublishSessionCreatedEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterCreate(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(sessionEventHandler, times(1)).remember(eq("1")); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionCreatedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verifyZeroInteractions(this.mockSession); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterCreateHandlesNullSessionWillNotPublishSessionCreatedEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(null); + + sessionEventHandler.afterCreate(mockEntryEvent); - verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, never()).getKey(); verify(mockEntryEvent, times(1)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(this.mockSession); + verify(sessionEventHandler, never()).remember(eq("1")); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionCreatedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); } @Test - public void isCreateWithLocalLoadCreateOperationReturnsFalse() { + @SuppressWarnings("unchecked") + public void afterCreateHandlesTombstoneWillNotPublishSessionCreatedEvent() { - SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.LOCAL_LOAD_CREATE, "12345", null, this.mockSession); + EntryEvent mockEntryEvent = mock(EntryEvent.class); - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + when(mockEntryEvent.getNewValue()).thenReturn(new Tombstone()); - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isFalse(); + sessionEventHandler.afterCreate(mockEntryEvent); - verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(this.mockSession); + verify(mockEntryEvent, times(1)).getNewValue(); + verify(sessionEventHandler, never()).remember(eq("1")); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionCreatedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); } @Test - public void isCreateWithRememberedSessionIdReturnsFalse() { + public void afterDeleteForgetsSessionIdPublishesSessionDeletedEventForSession() { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + when(this.mockSession.getId()).thenReturn("1"); - sessionEventHandler.remember("12345"); + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterDelete("1", this.mockSession); - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isFalse(); + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionDeletedEvent(eq(this.mockSession)); + verify(sessionEventHandler, times(1)).toSession(eq(this.mockSession), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDeletedEvent.class)); + } + + @Test + public void afterDeleteForgetsSessionIdPublishesSessionDeletedEventForSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterDelete("1", null); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionDeletedEvent(isA(SessionIdHolder.class)); + verify(sessionEventHandler, times(1)).toSession(isNull(), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDeletedEvent.class)); + } + + @Test(expected = IllegalStateException.class) + public void afterDeleteHandlesNullSessionAndNullSessionIdThrowsIllegalStateException() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + try { + sessionEventHandler.afterDelete(null, null); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("Session or the Session ID [null] must be known to trigger a Session event"); + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + //verify(sessionEventHandler, times(1)).forget(isNull()); + verify(sessionEventHandler, times(1)).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionDeletedEvent(any(Session.class)); + verify(sessionEventHandler, times(1)).toSession(isNull(), isNull()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); + } + } + + @Test + public void afterDestroyIsNullSafe() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + sessionEventHandler.afterDestroy(null); + + verify(sessionEventHandler, never()).forget(any()); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionDestroyedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterDestroyHandlesKnownSessionPublishesSessionDestroyedEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(this.mockSession); + when(this.mockSession.getId()).thenReturn("1"); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + when(this.mockSession.getId()).thenReturn("1"); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterDestroy(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockSession, times(1)).getId(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionDestroyedEvent(eq(this.mockSession)); + verify(sessionEventHandler, times(1)).toSession(eq(this.mockSession), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDestroyedEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterDestroyHandlesNullSessionPublishesSessionDestroyedEventWithSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(null); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterDestroy(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionDestroyedEvent(isA(SessionIdHolder.class)); + verify(sessionEventHandler, times(1)).toSession(isNull(), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDestroyedEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterDestroyHandlesTombstonePublishesSessionDestroyedEventWithSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(new Tombstone()); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterDestroy(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionDestroyedEvent(isA(SessionIdHolder.class)); + verify(sessionEventHandler, times(1)).toSession(isA(Tombstone.class), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionDestroyedEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterDestroyHandlesUnknownSessionWillNotPublishSessionDestroyedEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + + sessionEventHandler.afterDestroy(mockEntryEvent); - verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(this.mockSession); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionDestroyedEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); } @Test - public void isCreateWithUpdateOperationReturnsFalse() { + public void afterInvalidateIsNullSafe() { - SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); - Session mockOldValue = mock(Session.class); + sessionEventHandler.afterInvalidate(null); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.UPDATE, "12345", mockOldValue, this.mockSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isFalse(); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(mockOldValue); - verifyZeroInteractions(this.mockSession); + verify(sessionEventHandler, never()).forget(any()); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionExpiredEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); } @Test - public void isCreateWithTombstoneReturnsFalse() { + @SuppressWarnings("unchecked") + public void afterInvalidateHandlesKnownSessionPublishesSessionExpiredEvent() { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, new Tombstone()); + EntryEvent mockEntryEvent = mock(EntryEvent.class); - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(this.mockSession); + when(this.mockSession.getId()).thenReturn("1"); - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isFalse(); + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + when(this.mockSession.getId()).thenReturn("1"); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterInvalidate(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(this.mockSession, times(1)).getId(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionExpiredEvent(eq(this.mockSession)); + verify(sessionEventHandler, times(1)).toSession(eq(this.mockSession), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionExpiredEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterInvalidateHandlesNullSessionPublishesSessionExpiredEventUsingSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(null); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterInvalidate(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionExpiredEvent(isA(SessionIdHolder.class)); + verify(sessionEventHandler, times(1)).toSession(isNull(), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionExpiredEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterInvalidateHandlesTombstonePublishesSessionExpiredEventUsingSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + when(mockEntryEvent.getOldValue()).thenReturn(new Tombstone()); + + assertThat(sessionEventHandler.remember("1")).isTrue(); + assertThat(sessionEventHandler.isRemembered("1")).isTrue(); + + sessionEventHandler = spy(sessionEventHandler); + sessionEventHandler.afterInvalidate(mockEntryEvent); + + assertThat(sessionEventHandler.isRemembered("1")).isFalse(); + + verify(mockEntryEvent, times(2)).getKey(); + verify(mockEntryEvent, times(1)).getOldValue(); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, times(2)).getSessionRepository(); + verify(sessionEventHandler, times(1)).newSessionExpiredEvent(isA(SessionIdHolder.class)); + verify(sessionEventHandler, times(1)).toSession(isA(Tombstone.class), eq("1")); + verify(this.sessionRepository, times(1)).publishEvent(isA(SessionExpiredEvent.class)); + } + + @Test + @SuppressWarnings("unchecked") + public void afterInvalidateHandlesUnknownSessionWillNotPublishSessionExpiredEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn("1"); + + sessionEventHandler.afterInvalidate(mockEntryEvent); + + verify(mockEntryEvent, times(1)).getKey(); + verify(mockEntryEvent, never()).getOldValue(); + verifyZeroInteractions(this.mockSession); + verify(sessionEventHandler, times(1)).forget(eq("1")); + verify(sessionEventHandler, never()).getSessionRepository(); + verify(sessionEventHandler, never()).newSessionExpiredEvent(any(Session.class)); + verify(sessionEventHandler, never()).toSession(any(), any()); + verify(this.sessionRepository, never()).publishEvent(any(ApplicationEvent.class)); + } + + @Test + public void sessionCreateCreateExpireRecreatePublishesSessionEventsCreateExpireCreate() { + + ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + + AtomicInteger index = new AtomicInteger(0); + + Class[] expectedSessionEventTypes = { + SessionCreatedEvent.class, SessionExpiredEvent.class, SessionCreatedEvent.class + }; + + doAnswer(invocation -> { + + ApplicationEvent applicationEvent = invocation.getArgument(0); + + assertThat(applicationEvent).isInstanceOf(expectedSessionEventTypes[index.getAndIncrement()]); + + AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; + + assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); + assertThat(sessionEvent.getSessionId()).isEqualTo("123456789"); + assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); + + return null; + + }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); + + EntryEvent mockCreateEvent = + mockEntryEvent(Operation.CREATE, "123456789", null, this.mockSession); + + EntryEvent mockExpireEvent = + mockEntryEvent(Operation.INVALIDATE, "123456789", this.mockSession, null); + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + doReturn(mockApplicationEventPublisher).when(this.sessionRepository).getApplicationEventPublisher(); + when(this.mockSession.getId()).thenReturn("123456789"); + + sessionEventHandler.afterCreate(mockCreateEvent); + sessionEventHandler.afterCreate(mockCreateEvent); + sessionEventHandler.afterInvalidate(mockExpireEvent); + sessionEventHandler.afterCreate(mockCreateEvent); + + verify(mockCreateEvent, times(5)).getKey(); + verify(mockCreateEvent, times(5)).getNewValue(); + verify(mockCreateEvent, never()).getOldValue(); + verify(mockExpireEvent, times(2)).getKey(); + verify(mockExpireEvent, never()).getNewValue(); + verify(mockExpireEvent, times(1)).getOldValue(); + verify(this.mockSession, times(3)).getId(); + verify(mockApplicationEventPublisher, times(2)) + .publishEvent(isA(SessionCreatedEvent.class)); + verify(mockApplicationEventPublisher, times(1)) + .publishEvent(isA(SessionExpiredEvent.class)); + } + + private void testNewSessionEventIsCorrect( + Function sessionEventFactory, Class sessionEventType) { + + when(this.mockSession.getId()).thenReturn("1"); + + AbstractSessionEvent event = sessionEventFactory.apply(this.mockSession); + + assertThat(event).isInstanceOf(sessionEventType); + assertThat(event.getSource()).isEqualTo(this.sessionRepository); + assertThat(event.getSession()).isEqualTo(this.mockSession); + assertThat(event.getSessionId()).isEqualTo("1"); + + verify(this.mockSession, times(1)).getId(); + } + + @Test + public void newSessionCreatedEventIsCorrect() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + assertThat(sessionEventHandler).isNotNull(); + + testNewSessionEventIsCorrect(sessionEventHandler::newSessionCreatedEvent, SessionCreatedEvent.class); + + verify(sessionEventHandler, times(1)).getSessionRepository(); + } + + @Test + public void newSessionDeletedEventIsCorrect() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + assertThat(sessionEventHandler).isNotNull(); + + testNewSessionEventIsCorrect(sessionEventHandler::newSessionDeletedEvent, SessionDeletedEvent.class); + + verify(sessionEventHandler, times(1)).getSessionRepository(); + } + + @Test + public void newSessionDestroyedEventIsCorrect() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + assertThat(sessionEventHandler).isNotNull(); + + testNewSessionEventIsCorrect(sessionEventHandler::newSessionDestroyedEvent, SessionDestroyedEvent.class); + + verify(sessionEventHandler, times(1)).getSessionRepository(); + } + + @Test + public void newSessionExpiredEventIsCorrect() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = + spy(this.sessionRepository.newSessionEventHandler()); + + assertThat(sessionEventHandler).isNotNull(); + + testNewSessionEventIsCorrect(sessionEventHandler::newSessionExpiredEvent, SessionExpiredEvent.class); + + verify(sessionEventHandler, times(1)).getSessionRepository(); + } + + @Test + public void isRememberedWithKnownSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.getCachedSessionIds().add(ObjectUtils.nullSafeHashCode(1))).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isTrue(); + } + + @Test + public void isRememberedWithUnknownSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + assertThat(sessionEventHandler.isRemembered(1)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + } + + @Test + @SuppressWarnings("unchecked") + public void forgetsEntryEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn(1); + + assertThat(sessionEventHandler.getCachedSessionIds().add(ObjectUtils.nullSafeHashCode(1))).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isTrue(); + assertThat(sessionEventHandler.forget(mockEntryEvent)).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + + verify(mockEntryEvent, times(1)).getKey(); + } + + @Test + @SuppressWarnings("unchecked") + public void forgetEntryEventWithNullKey() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn(null); + + assertThat(sessionEventHandler.forget(mockEntryEvent)).isFalse(); + + verify(mockEntryEvent, times(1)).getKey(); + } + + @Test + @SuppressWarnings("unchecked") + public void forgetNullEntryEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.forget(null)).isFalse(); + } + + @Test + public void forgetKnownSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + assertThat(sessionEventHandler.getCachedSessionIds().add(ObjectUtils.nullSafeHashCode(1))).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isTrue(); + assertThat(sessionEventHandler.forget(1)).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + } + + @Test + public void forgetNullSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.forget((Object) null)).isFalse(); + } + + @Test + public void forgetUnknownSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.forget(1)).isFalse(); + } + + @Test + @SuppressWarnings("unchecked") + public void remembersEntryEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getKey()).thenReturn(1); + when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession); + + assertThat(sessionEventHandler.isRemembered(1)).isFalse(); + assertThat(sessionEventHandler.remember(mockEntryEvent)).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isTrue(); - verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, times(1)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(this.mockSession); } @Test - public void isCreateWithNullReturnsFalse() { + @SuppressWarnings("unchecked") + public void rememberEntryEventWithInvalidSessionId() { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, null); + assertThat(sessionEventHandler).isNotNull(); - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); + EntryEvent mockEntryEvent = mock(EntryEvent.class); - assertThat(sessionEventHandler.isCreate(mockEntryEvent)).isFalse(); + when(mockEntryEvent.getKey()).thenReturn(null); + when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession); + + assertThat(sessionEventHandler.remember(mockEntryEvent)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); - verify(mockEntryEvent, times(1)).getOperation(); verify(mockEntryEvent, times(1)).getKey(); verify(mockEntryEvent, times(1)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); } @Test - public void toSessionWithSession() { + @SuppressWarnings("unchecked") + public void rememberEntryEventWithInvalidValue() { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + assertThat(sessionEventHandler).isNotNull(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(new Tombstone()); + + assertThat(sessionEventHandler.remember(mockEntryEvent)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + } + + @Test + @SuppressWarnings("unchecked") + public void rememberEntryEventWithNullValue() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(null); + + assertThat(sessionEventHandler.remember(mockEntryEvent)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + + verify(mockEntryEvent, never()).getKey(); + verify(mockEntryEvent, times(1)).getNewValue(); + } + + @Test + public void rememberNullEntryEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + + assertThat(sessionEventHandler.remember(null)).isFalse(); + assertThat(sessionEventHandler.getCachedSessionIds()).isEmpty(); + } + + @Test + public void rememberNullSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isRemembered(null)).isFalse(); + assertThat(sessionEventHandler.remember((Object) null)).isTrue(); + assertThat(sessionEventHandler.isRemembered(null)).isTrue(); + assertThat(sessionEventHandler.getCachedSessionIds()).containsExactly(0); + } + + @Test + public void remembersSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isRemembered(1)).isFalse(); + assertThat(sessionEventHandler.remember(1)).isTrue(); + assertThat(sessionEventHandler.isRemembered(1)).isTrue(); + } + + @Test + public void isSessionWithEntryEventContainingNull() { + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(null); + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(mockEntryEvent)).isFalse(); + } + + @Test + public void isSessionWithEntryEventContainingSession() { + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(this.mockSession); + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(mockEntryEvent)).isTrue(); + } + + @Test + public void isSessionWithEntryEventContainingTombstone() { + + EntryEvent mockEntryEvent = mock(EntryEvent.class); + + when(mockEntryEvent.getNewValue()).thenReturn(new Tombstone()); + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(mockEntryEvent)).isFalse(); + } + + @Test + public void isSessionWithNullEntryEvent() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(null)).isFalse(); + } + + @Test + public void isSessionWithNull() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession((Object) null)).isFalse(); + } + + @Test + public void isSessionWithSession() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(this.mockSession)).isTrue(); + } + + @Test + public void isSessionWithTombstone() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); + assertThat(sessionEventHandler.isSession(new Tombstone())).isFalse(); + } + + @Test + public void toSessionWithSessionAndSessionId() { + + SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + + assertThat(sessionEventHandler).isNotNull(); assertThat(sessionEventHandler.toSession(this.mockSession, "12345")).isSameAs(this.mockSession); } @Test public void toSessionWithTombstoneAndSessionId() { - Tombstone tombstone = new Tombstone(); - SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + assertThat(sessionEventHandler).isNotNull(); + + Tombstone tombstone = new Tombstone(); + Session session = sessionEventHandler.toSession(tombstone, "12345"); assertThat(session).isNotNull(); @@ -817,34 +1664,61 @@ public class AbstractGemFireOperationsSessionRepositoryTests { assertThat(session.getId()).isEqualTo("12345"); } - @Test(expected = IllegalStateException.class) - public void toSessionWithNullSessionAndNullSessionId() { + private void testToSessionWithNullSessionAndInvalidSessionId(String sessionId) { SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + assertThat(sessionEventHandler).isNotNull(); + try { - sessionEventHandler.toSession(null, null); + sessionEventHandler.toSession(null, sessionId); } catch (IllegalStateException expected) { - assertThat(expected).hasMessage("Minimally, the Session ID [null] must be known to trigger a Session event"); + assertThat(expected) + .hasMessage("Session or the Session ID [%s] must be known to trigger a Session event", + sessionId); + assertThat(expected).hasNoCause(); throw expected; } } + @Test(expected = IllegalStateException.class) + public void toSessionWithNullSessionAndEmptySessionId() { + testToSessionWithNullSessionAndInvalidSessionId(""); + } + + @Test(expected = IllegalStateException.class) + public void toSessionWithNullSessionAndNullSessionId() { + testToSessionWithNullSessionAndInvalidSessionId(null); + } + @Test(expected = IllegalStateException.class) public void toSessionWithNullSessionAndUnspecifiedSessionId() { + testToSessionWithNullSessionAndInvalidSessionId(null); + } - SessionEventHandlerCacheListenerAdapter sessionEventHandler = this.sessionRepository.newSessionEventHandler(); + @Test + public void constructSessionIdInterestRegisteringCacheListener() { + + SessionIdInterestRegisteringCacheListener listener = + new SessionIdInterestRegisteringCacheListener(this.sessionRepository); + + assertThat(listener).isNotNull(); + assertThat(listener.getSessionRepository()).isSameAs(this.sessionRepository); + } + + @Test(expected = IllegalArgumentException.class) + public void constructSessionIdInterestRegisteringCacheListenerWithNull() { try { - sessionEventHandler.toSession(null, " "); + new SessionIdInterestRegisteringCacheListener(null); } - catch (IllegalStateException expected) { + catch (IllegalArgumentException expected) { - assertThat(expected).hasMessage("Minimally, the Session ID [ ] must be known to trigger a Session event"); + assertThat(expected).hasMessage("SessionRepository is required"); assertThat(expected).hasNoCause(); throw expected; @@ -852,587 +1726,66 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void afterCreateHandlesNullEntryEvent() { + public void constructSessionIdInterestRegisteringCacheListenerUsingSessionRepository() { - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + SessionIdInterestRegisteringCacheListener listener = this.sessionRepository.newSessionIdInterestRegistrar(); - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - sessionEventHandler.afterCreate(null); - - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - verifyZeroInteractions(mockApplicationEventPublisher); + assertThat(listener).isNotNull(); + assertThat(listener.getSessionRepository()).isSameAs(this.sessionRepository); } @Test @SuppressWarnings("unchecked") - public void afterCreateWithNewSessionPublishesSessionCreatedEvent() { + public void sessionIdInterestRegisteringCacheListerAfterCreateCallsRegisterInterest() { - when(this.mockSession.getId()).thenReturn("12345"); + SessionIdInterestRegisteringCacheListener listener = + spy(this.sessionRepository.newSessionIdInterestRegistrar()); - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + EntryEvent mockEntryEvent = mock(EntryEvent.class); - doAnswer(invocation -> { + when(mockEntryEvent.getKey()).thenReturn("1"); - ApplicationEvent applicationEvent = invocation.getArgument(0); + listener.afterCreate(mockEntryEvent); - assertThat(applicationEvent).isInstanceOf(SessionCreatedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, times(2)).getKey(); - verify(mockEntryEvent, times(2)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(this.mockSession, times(1)).getId(); - verify(sessionEventHandler, times(1)) - .handleCreated(eq("12345"), eq(this.mockSession)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionCreatedEvent.class)); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForCreateOperationDoesNotPublishSessionCreatedEventWhenSessionIdIsRemembered() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.CREATE, "12345", null, this.mockSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - sessionEventHandler.remember("12345"); - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); + verify(listener, times(1)).getSessionRepository(); verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - verifyZeroInteractions(this.mockSession); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForLocalLoadCreateOperationDoesNotPublishSessionCreatedEvent() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.LOCAL_LOAD_CREATE, "12345", null, this.mockSession); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.REPLICATE)); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - verifyZeroInteractions(this.mockSession); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForDestroyOperationDoesNotPublishSessionCreatedEvent() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - mockEntryEvent(Operation.DESTROY, "12345", null, null); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForInvalidateOperationDoesNotPublishSessionCreatedEvent() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - mockEntryEvent(Operation.INVALIDATE, "12345", null, this.mockSession); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - verifyZeroInteractions(this.mockSession); - } - - @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void afterCreateForUpdateOperationDoesNotPublishSessionCreatedEvent() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - Session mockOldValue = mock(Session.class); - - EntryEvent mockEntryEvent = - mockEntryEvent(Operation.UPDATE, "12345", mockOldValue, this.mockSession); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, never()).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verifyZeroInteractions(mockOldValue); - verifyZeroInteractions(this.mockSession); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterCreateWithTombstoneDoesNotPublishSessionCreatedEvent() { - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = mockEntryEvent(Operation.CREATE, "12345", null, new Tombstone()); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - sessionEventHandler.afterCreate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getOperation(); - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, times(1)).getNewValue(); - verify(mockEntryEvent, never()).getOldValue(); - verify(sessionEventHandler, never()).handleCreated(anyString(), any()); - } - - @Test - public void afterDestroyHandlesNullEntryEvent() { - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - sessionEventHandler.afterDestroy(null); - - verify(sessionEventHandler, never()).handleDestroyed(anyString(), any()); - verifyZeroInteractions(mockApplicationEventPublisher); + verify(this.sessionRepository).registerInterest(eq("1")); } @Test @SuppressWarnings("unchecked") - public void afterDestroyWithSessionPublishesSessionDestroyedEvent() { + public void sessionIdInterestRegisteringCacheListerAfterDestroyCallsUnregisterInterest() { - when(this.mockSession.getId()).thenReturn("12345"); + SessionIdInterestRegisteringCacheListener listener = + spy(this.sessionRepository.newSessionIdInterestRegistrar()); - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + EntryEvent mockEntryEvent = mock(EntryEvent.class); - doAnswer(invocation -> { + when(mockEntryEvent.getKey()).thenReturn("1"); - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionDestroyedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.DESTROY, "12345", this.mockSession, null); - - sessionEventHandler.afterDestroy(mockEntryEvent); + listener.afterDestroy(mockEntryEvent); + verify(listener, times(1)).getSessionRepository(); verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(this.mockSession, times(1)).getId(); - verify(sessionEventHandler, times(1)) - .handleDestroyed(eq("12345"), isA(Session.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionDestroyedEvent.class)); + verify(this.sessionRepository).unregisterInterest(eq("1")); } @Test @SuppressWarnings("unchecked") - public void afterDestroyWithSessionIdPublishesSessionDestroyedEvent() { + public void sessionIdInterestRegisteringCacheListerAfterInvalidateCallsUnregisterInterest() { - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); + SessionIdInterestRegisteringCacheListener listener = + spy(this.sessionRepository.newSessionIdInterestRegistrar()); - doAnswer(invocation -> { + EntryEvent mockEntryEvent = mock(EntryEvent.class); - ApplicationEvent applicationEvent = invocation.getArgument(0); + when(mockEntryEvent.getKey()).thenReturn("1"); - assertThat(applicationEvent).isInstanceOf(SessionDestroyedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - Session session = sessionEvent.getSession(); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isEqualTo("12345"); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.DESTROY, "12345", null, null); - - sessionEventHandler.afterDestroy(mockEntryEvent); + listener.afterInvalidate(mockEntryEvent); + verify(listener, times(1)).getSessionRepository(); verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(sessionEventHandler, times(1)) - .handleDestroyed(eq("12345"), isA(Session.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionDestroyedEvent.class)); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterDestroyWithTombstonePublishesSessionDestroyedEventWithSessionId() { - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - doAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionDestroyedEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - Session session = sessionEvent.getSession(); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isEqualTo("12345"); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = mockEntryEvent(Operation.DESTROY, "12345", new Tombstone(), null); - - sessionEventHandler.afterDestroy((EntryEvent) mockEntryEvent); - - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(sessionEventHandler, times(1)) - .handleDestroyed(eq("12345"), isA(Session.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionDestroyedEvent.class)); - } - - @Test - public void afterInvalidateHandlesNullEntryEvent() { - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - sessionEventHandler.afterInvalidate(null); - - verify(sessionEventHandler, never()).handleExpired(anyString(), any()); - verifyZeroInteractions(mockApplicationEventPublisher); - } - - @Test - @SuppressWarnings("unchecked") - public void afterInvalidateWithSessionPublishesSessionExpiredEvent() { - - when(this.mockSession.getId()).thenReturn("12345"); - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - doAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionExpiredEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSession()).isEqualTo(this.mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.INVALIDATE, "12345", mockSession, null); - - sessionEventHandler.afterInvalidate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(this.mockSession, times(1)).getId(); - verify(sessionEventHandler, times(1)) - .handleExpired(eq("12345"), eq(this.mockSession)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionExpiredEvent.class)); - } - - @Test - @SuppressWarnings("unchecked") - public void afterInvalidateWithSessionIdPublishesSessionExpiredEvent() { - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - doAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionExpiredEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - Session session = sessionEvent.getSession(); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isEqualTo("12345"); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = - this.mockEntryEvent(Operation.INVALIDATE, "12345", null, null); - - sessionEventHandler.afterInvalidate(mockEntryEvent); - - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(sessionEventHandler, times(1)) - .handleExpired(eq("12345"), isA(Session.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionExpiredEvent.class)); - } - - @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void afterInvalidateWithTombstonePublishesSessionExpiredEventWithSessionId() { - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - doAnswer(invocation -> { - - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(SessionExpiredEvent.class); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - Session session = sessionEvent.getSession(); - - assertThat(session).isNotNull(); - assertThat(session.getId()).isEqualTo("12345"); - assertThat(sessionEvent.getSessionId()).isEqualTo("12345"); - assertThat(sessionEvent.getSource()).isEqualTo(this.sessionRepository); - - return null; - - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockEntryEvent = mockEntryEvent(Operation.INVALIDATE, "12345", new Tombstone(), null); - - sessionEventHandler.afterInvalidate((EntryEvent) mockEntryEvent); - - verify(mockEntryEvent, times(1)).getKey(); - verify(mockEntryEvent, never()).getNewValue(); - verify(mockEntryEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(sessionEventHandler, times(1)) - .handleExpired(eq("12345"), isA(Session.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionExpiredEvent.class)); - } - - @Test - public void sessionCreateCreateExpireRecreatePublishesSessionEventsCreateExpireCreate() { - - when(this.mockSession.getId()).thenReturn("123456789"); - - ApplicationEventPublisher mockApplicationEventPublisher = mock(ApplicationEventPublisher.class); - - doAnswer(new Answer() { - - int index = 0; - - Class[] expectedSessionTypes = { - SessionCreatedEvent.class, SessionExpiredEvent.class, SessionCreatedEvent.class - }; - - public Void answer(InvocationOnMock invocation) throws Throwable { - ApplicationEvent applicationEvent = invocation.getArgument(0); - - assertThat(applicationEvent).isInstanceOf(this.expectedSessionTypes[this.index++]); - - AbstractSessionEvent sessionEvent = (AbstractSessionEvent) applicationEvent; - - assertThat(sessionEvent.getSession()).isEqualTo(mockSession); - assertThat(sessionEvent.getSessionId()).isEqualTo("123456789"); - assertThat(sessionEvent.getSource()) - .isEqualTo(AbstractGemFireOperationsSessionRepositoryTests.this.sessionRepository); - - return null; - } - }).when(mockApplicationEventPublisher).publishEvent(isA(ApplicationEvent.class)); - - withRegion(this.sessionRepository, mockRegion("Example", DataPolicy.EMPTY)); - - this.sessionRepository.setApplicationEventPublisher(mockApplicationEventPublisher); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - SessionEventHandlerCacheListenerAdapter sessionEventHandler = - spy(this.sessionRepository.newSessionEventHandler()); - - EntryEvent mockCreateEvent = - this.mockEntryEvent(Operation.CREATE, "123456789", null, this.mockSession); - - EntryEvent mockExpireEvent = - this.mockEntryEvent(Operation.INVALIDATE, "123456789", this.mockSession, null); - - sessionEventHandler.afterCreate(mockCreateEvent); - sessionEventHandler.afterCreate(mockCreateEvent); - sessionEventHandler.afterInvalidate(mockExpireEvent); - sessionEventHandler.afterCreate(mockCreateEvent); - - assertThat(this.sessionRepository.getApplicationEventPublisher()).isSameAs(mockApplicationEventPublisher); - - verify(mockCreateEvent, times(3)).getOperation(); - verify(mockCreateEvent, times(5)).getKey(); - verify(mockCreateEvent, times(4)).getNewValue(); - verify(mockCreateEvent, never()).getOldValue(); - verify(mockExpireEvent, never()).getOperation(); - verify(mockExpireEvent, times(1)).getKey(); - verify(mockExpireEvent, never()).getNewValue(); - verify(mockExpireEvent, times(1)).getOldValue(); - verify(this.mockLog, never()).error(anyString(), any(Throwable.class)); - verify(this.mockSession, times(3)).getId(); - verify(sessionEventHandler, times(2)) - .handleCreated(eq("123456789"), eq(this.mockSession)); - verify(sessionEventHandler, times(1)) - .handleExpired(eq("123456789"), eq(this.mockSession)); - verify(mockApplicationEventPublisher, times(2)) - .publishEvent(isA(SessionCreatedEvent.class)); - verify(mockApplicationEventPublisher, times(1)) - .publishEvent(isA(SessionExpiredEvent.class)); + verify(this.sessionRepository).unregisterInterest(eq("1")); } @Test @@ -2186,7 +2539,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { @Test @SuppressWarnings("all") - public void sessionEqualsDifferentSessionBasedOnId() { + public void sessionEqualsDifferentLogicalSessionBasedOnId() { GemFireSession sessionOne = new GemFireSession("1"); @@ -2214,7 +2567,7 @@ public class AbstractGemFireOperationsSessionRepositoryTests { } @Test - public void sessionIsNotEqualToDifferentSessionBasedOnId() { + public void sessionNotEqualToDifferentSessionBasedOnId() { GemFireSession sessionOne = new GemFireSession("1");